一、Dijskstra算法
参考我之前一篇博文:http://www.cnblogs.com/Oloo/articles/3614810.html
或者《算法导论》第24章单源最短路 Dijskstra算法部分
二、问题描述
POJ 2253:http://poj.org/problem?id=2253
一共有两只青蛙,分别位于湖中两块不同的石头上,一只青蛙想要跳到另一只青蛙那里,通过湖中的石头。现在给出湖中石头的坐标,要求青蛙至少每次能跳多远,才能顺利到达另一只青蛙那里。
三、问题分析
编程之前需要解决三个问题:
问题一、图的构造?
问题二、单源最短路or每对顶点间最短路 / BellmanFord or Dijskstra?
问题三、如何修改Dijskstra算法?
问题一:这道题目图的构造还是不算难的!以每块石头为节点,任意两块石头之间都有边,边的权重可以根据这两块石头的坐标来计算,这样图就构造出来了。
问题二:我们要求青蛙至少要具备的最少的跳跃距离。
首先,我考虑,这个题目还是给出了起点和终点,即两只青蛙的坐标。所以还是考虑单源最短路的相关算法通过修改来解决这个问题。
因为每条边的权重肯定是正的,即不存在负权边,所以,立马考虑使用Dijskstra算法来做。
问题三:通过考虑给出的第二个样例,我发现要求的值并不在最短路径上,所以,这个问题不是个求最短的问题。所以,就可以立马想到修改Dijskstra算法的Relax的条件,即什么时候要修改目标节点v的D值?通过观察,我找到了这样的规则,在松弛u邻接节点v的时候,如果D[v] > D[u] && D[v] > W[u,v],那么这时候就需要进行修改D[v],将D[v] = max{D[u],W[u,v]},至此,这道题就可以说解决了。
四、程序实现
1 #include<iostream> 2 #include<iomanip> 3 #include<cmath> 4 using namespace std; 5 #define INF 1000000000 6 struct stone 7 { 8 int x,y; 9 } *stones; 10 int N;//石头数目 11 int *value;//顶点D值 12 int *flag; 13 int edges[202][202]; 14 15 void input() 16 { 17 value = new int[N]; 18 flag = new int[N]; 19 for(int i = 0;i < N;i++) 20 { 21 value[i] = INF; 22 flag[i] = 1; 23 } 24 value[0] = 0; 25 26 stones = new stone[N]; 27 for(int i = 0;i < N;i++) 28 cin >> stones[i].x >> stones[i].y; 29 30 for(int i = 0;i < N;i++) 31 for(int j = 0;j < N;j++) 32 { 33 edges[i][j] = (stones[j].x - stones[i].x)*(stones[j].x - stones[i].x) 34 + (stones[j].y - stones[i].y)*(stones[j].y - stones[i].y); 35 } 36 } 37 38 int ExtractMin() 39 { 40 int min = INF; int index = -1; 41 for(int i = 0;i < N;i++) 42 { 43 if(min > value[i] && flag[i]) 44 { 45 min = value[i]; 46 index = i; 47 } 48 } 49 return index; 50 } 51 52 int Mmax(int a ,int b) 53 { 54 if(a > b) return a; 55 else return b; 56 } 57 58 void Relax(int u , int v) 59 { 60 if(value[v] > edges[u][v] && value[v] > value[u]) 61 { 62 value[v] = Mmax(edges[u][v],value[u]); 63 } 64 } 65 66 void Dijskstra() 67 { 68 int k = 0; 69 while(k != N) 70 { 71 int u = ExtractMin(); 72 if(flag[u]) 73 { 74 flag[u] = 0; 75 for(int v = 0;v < N;v++) 76 Relax(u , v); 77 } 78 k++; 79 } 80 } 81 82 int main() 83 { 84 int t = 1; 85 while(cin >> N) 86 { 87 if(N == 0) break; 88 input(); 89 Dijskstra(); 90 cout<<setiosflags(ios::fixed); 91 cout <<"Scenario #"<<t<<endl; 92 cout <<"Frog Distance = "<< setprecision(3)<< sqrt((double)(value[1])) << endl; 93 cout << endl; 94 t++; 95 } 96 return 0; 97 }
四、出现问题
问题1——精度问题:
这个题目要求保留三位小数,在这里赘述一下C++的cout怎么设置保留精度<还是c的printf用着舒服>,需要的代码如下:
1 #include<iomanip> 2 3 cout<<setiosflags(ios::fixed); 4 5 setprecision(3)
问题2——POJ 编译问题
这道题,第一次交的时候,以为一次就可以AC呢,结果CE了,看了编译信息发现,原来没有包含cmath头文件,就直接使用了sqrt(),不知道为什么我的VS 2012没报错。。
这里包含了文件cmath之后,其实还有问题,那就是sqrt这个函数被重载过的,所以要指明想要调用哪一个函数,即sqrt((double)parameter),指明就可以,这种会在传入int型参数的时候出现。
五、感想
自己通过自己独立分析问题,自己独立编程实现,最后这个题目AC,这个节奏还是很好的~
最后,YZY,我想你!~
浙公网安备 33010602011771号