ACM/ICPC 之 最短路径-Bellman Ford范例(POJ1556-POJ2240)
两道Bellman Ford解最短路的范例,Bellman Ford只是一种最短路的方法,两道都可以用dijkstra, SPFA做。
Bellman Ford解法是将每条边遍历一次,遍历一次所有边可以求得一点到任意一点经过一条边的最短路,遍历两次可以求得一点到任意一点经过两条边的最短路...如 此反复,当遍历m次所有边后,则可以求得一点到任意一点经过m条边后的最短路(有点类似离散数学中邻接矩阵的连通性判定)
POJ1556-The Doors
初学就先看POJ2240吧
题意:求从(0,5)到(10,5)的最短折线距离,中间会给最多十八道墙。
题解:本题设计简单的计算几何知识和最短路的知识,读完题后要将墙的端点看做一个坐标,先求出所有的可行边,然后做一次最短路就可以了
1 //门 2 //POJ1556-ZOJ1721 3 //简单几何+最短路 4 //Time:0Ms Memory:204K 5 #include<iostream> 6 #include<cstring> 7 #include<cstdio> 8 #include<algorithm> 9 #include<cmath> 10 using namespace std; 11 #define INF 0x7f7f7f7f 12 #define MAX 20*5 13 #define POW2(x) ((x)*(x)) 14 #define DIS(i,j) sqrt(POW2(i.x - j.x) + POW2(i.y - j.y)) 15 #define P(i,j) (4*i+j) //第i组第j个点的序列号 16 struct Point { 17 double x, y; 18 }p[MAX]; 19 struct Edge { 20 int u, v; 21 double d; 22 }e[MAX*MAX]; 23 int n, m; 24 double d[MAX]; 25 int en; //edge_num 26 //判断两点是否能在maxn组点内连通 27 bool access(Point p1, Point p2, int maxn) 28 { 29 for (int i = 0; i < maxn; i++) 30 if (p[P(i, 1)].x > p1.x && p[P(i, 1)].x < p2.x) //该组点在两点之间 31 { 32 //算出线段在该横坐标下的纵坐标 33 double y = (p1.y - p2.y) / (p2.x - p1.x) * (p2.x - p[P(i, 1)].x) + p2.y; 34 if (y > p[P(i, 4)].y || y < p[P(i, 1)].y || (y > p[P(i, 2)].y && y < p[P(i, 3)].y)) //相交 35 return false; 36 } 37 return true; 38 } 39 //记录x组第y个点作为终点的线段 40 void add(int x, int y) 41 { 42 for (int k = 0; k <= 4 * x; k++) 43 if (access(p[k], p[P(x, y)], x)) { 44 e[en].u = k; 45 e[en].v = P(x, y); 46 e[en++].d = DIS(p[e[en].u], p[e[en].v]); 47 } 48 } 49 void bellman_ford(int x) 50 { 51 memset(d, INF, sizeof(d)); 52 d[0] = 0; 53 for (int i = 0; i <= n; i++) //扩展n+1次 54 for (int j = 0; j < en; j++) //遍历每条边 55 d[e[j].v] = min(d[e[j].u] + e[j].d, d[e[j].v]); 56 } 57 int main() 58 { 59 //起点 60 p[0].x = 0; 61 p[0].y = 5; 62 while (scanf("%d", &n), n != -1) 63 { 64 en = 0; //Init 65 double x, y; 66 for (int i = 0; i < n; i++) 67 { 68 scanf("%lf", &x); 69 for (int j = 1; j <= 4; j++) 70 { 71 scanf("%lf", &y); 72 p[P(i,j)].x = x; 73 p[P(i,j)].y = y; 74 add(i, j); 75 } 76 } 77 //终点 78 p[P(n, 1)].x = 10; 79 p[P(n, 1)].y = 5; 80 add(n, 1); 81 82 bellman_ford(0); 83 printf("%.2f\n", d[P(n, 1)]); 84 } 85 86 return 0; 87 }
POJ2240-Arbitrage
题意:从一种货币A经过多次转换后可以得到更多的货币A,则称为套汇,求给定货币转换语句,判断是否存在套汇。
题解:将货币看做结点,转换比率看做路长(乘积关系),建立一个图模型就可以知道实际上是在求是否存在最长路的路长超过1。
1 //套汇 2 //POJ2240-ZOJ1092 3 //求最长路(乘积) - 回路 路长 > 1 则为套汇 4 //Time:63Ms Memory:200K 5 #include<iostream> 6 #include<cstring> 7 #include<cstdio> 8 #include<algorithm> 9 using namespace std; 10 #define MAX 32 11 #define MAXS 20 12 struct Edge { 13 int u, v; 14 double d; 15 }e[MAX*MAX]; 16 int n, m; 17 char city[MAX][MAXS]; 18 double d[MAX]; //到某一点的最长路 19 int find(char s[MAXS]) 20 { 21 for (int i = 0; i < n;i++) 22 if (!strcmp(s, city[i])) return i; 23 return -1; 24 } 25 //是否套汇 26 bool bellman_ford(int x) 27 { 28 memset(d, 0, sizeof(d)); 29 d[x] = 1; 30 for (int i = 1; i <= n; i++) //最多经过n条边回到x(若更多次变更也只能是套汇) 31 for (int j = 0; j < m; j++) 32 { 33 d[e[j].v] = max(d[e[j].u] * e[j].d, d[e[j].v]); 34 if (d[x] > 1) return true; 35 } 36 return false; 37 } 38 int main() 39 { 40 int cas = 1; 41 while (scanf("%d", &n), n) 42 { 43 for (int i = 0; i < n; i++) 44 scanf("%s", city[i]); 45 scanf("%d", &m); 46 for (int i = 0; i < m; i++) 47 { 48 char s1[MAXS], s2[MAXS]; 49 double dis; 50 scanf("%s%lf%s", s1, &dis, s2); 51 e[i].u = find(s1); 52 e[i].v = find(s2); 53 e[i].d = dis; 54 } 55 56 bool flag = false; 57 for (int i = 0; i < n; i++) 58 { 59 if (bellman_ford(i)) { 60 flag = true; 61 break; 62 } 63 } 64 if (flag == true) 65 printf("Case %d: Yes\n", cas++); 66 else printf("Case %d: No\n", cas++); 67 } 68 return 0; 69 }
他坐在湖边,望向天空,她坐在对岸,盯着湖面