从起点房间1出发,初始有100的能量,达到每个房间会增加或者消耗一定的能量,问能否以大于0的能量到达点n(中间过程的能量也必须是大于0的)。并且每个房间可经过多次。
Bellman-Ford算法简介:
首先,对每个点相连的边进行记录,然后依次进行松弛操作,即
从源点s可达的所有顶点如果 存在最短路径,则这些最短路径构成一个以s为根的最短路径树。Bellman-Ford算法的迭代松弛操作,实际上就是按顶点距离s的层次,逐层生成这棵最短路径树的过程。
在对每条边进行1遍松弛的时候,生成了从s出发,层次至多为1的那些树枝。也就是说,找到了与s至多有1条边相联的那些顶点的最短路径;对每条边进行第2遍松弛的时候,生成了第2层次的树枝,就是说找到了经过2条边相连的那些顶点的最短路径……。因为最短路径最多只包含|v|-1 条边,所以,只需要循环|v|-1 次。
每实施一次松弛操作,最短路径树上就会有一层顶点达到其最短距离,此后这层顶点的最短距离值就会一直保持不变,不再受后续松弛操作的影响。(但是,每次还要判断松弛,这里浪费了大量的时间,怎么优化?单纯的优化是否可行?)
如果没有负权回路,由于最短路径树的高度最多只能是|v|-1,所以最多经过|v|-1遍松弛操作后,所有从s可达的顶点必将求出最短距离。如果 d[v]仍保持 +∞,则表明从s到v不可达。
如果有负权回路,那么第 |v|-1 遍松弛操作仍然会成功,这时,负权回路上的顶点不会收敛。
例如对于上图,边上方框中的数字代表权值,顶点A,B,C之间存在负权回路。S是源点,顶点中数字表示运行Bellman-Ford算法后各点的最短距离估计值。
此时d[a]的值为1,大于d[c]+w(c,a)的值-2,由此d[a]可以松弛为-2,然后d又可以松弛为-5,d[c]又可以松弛为-7.下一个周期,d[a]又可以更新为更小的值,这个过程永远不会终止。因此,在迭代求解最短路径阶段结束后,可以通过检验边集E的每条边(u,v)是否满足关系式 d[v]> d[u]+ w(u,v) 来判断是否存在负权回路。
1 从起点房间1出发,初始有100的能量,达到每个房间会增加或者消耗一定的能量,问能否以大于0的能量到达点n(中间过程的能量也必须是大于0的)。并且每个房间可经过多次。
2
3 我的做法是先判断1到n点的连通性,在连通的前提下,再用bellman_ford算出点n到点1的最长路径。因为一个点可以经过多次,所以有可能会有正环,此时用bellman的优势就体现出来了。如果有正环,或者点n到1的路径大于0,则可判定可达,否则判定不可达。
4
5 #include <stdio.h>
6 #include <string.h>
7 int map[101][101],map1[101][101];
8 int cost[101],d[101];
9 int n,p;
10 struct edge
11 {
12 int s,e;
13 }e[10000];
14
15 void floyd()
16 {
17 int i,j,k;
18 for(k=1;k<=n;k++)
19 for(i=1;i<=n;i++)
20 for(j=1;j<=n;j++)
21 if(map1[i][k]&&map1[k][j])
22 map1[i][j]=1;
23 map1[n][n]=1;
24 }
25
26 bool Bellman_Ford()
27 {
28 floyd();
29 if(!map1[1][n])
30 return false;
31 int i,j,k,l;
32 for(i=2;i<=n;i++)
33 d[i]=-1000000;
34 d[1]=100;
35 int relax;
36 for(k=1;k<n;k++)//
37 {
38 relax=0;
39 for(l=0;l<p;l++)
40 {
41 i=e[l].s;
42 j=e[l].e;
43 if(map[i][j]&&d[j]<d[i]+cost[j]&&d[i]+cost[j]>0&&map1[j][n])
44 {
45 d[j]=d[i]+cost[j];
46 relax=1;
47 if(j==n&&d[n]>0)
48 return true;
49 }
50 }
51 if(!relax) break;
52 }
53 if(d[n]>0||k==n) return true;
54 else return false;
55 }
56
57 int main()
58 {
59 int i,j,st,ed,v,t;
60 while(scanf("%d",&n)&&n!=-1)
61 {
62 memset(map,0,sizeof(map));
63 memset(map1,0,sizeof(map1));
64 p=0;
65 for(i=1;i<=n;i++)
66 {
67 scanf("%d %d",&cost[i],&t);
68 int a;
69 for(j=0;j<t;j++)
70 {
71 scanf("%d",&a);
72 e[p].s=i;
73 e[p++].e=a;
74 map[i][a]=1;
75 map1[i][a]=1;
76 }
77 }
78 if(Bellman_Ford())
79 printf("winnable\n");
80 else
81 printf("hopeless\n");
82 }
83 }
由于bellman算法效率比较低,因此还有改进的SPFA算法
#include <stdio.h> #include <stdlib.h> #include <iostream> #include <string.h> #include <queue> #include <limits.h> using namespace std; int n; int map[110][110]; int en[110]; int SPFA() { queue<int> q; int i,k,max,now,j; int used[110],dis[110],in[110]; for(i=1; i<=n; i++) dis[i] = 0; memset(in,0,sizeof(in)); memset(used,0,sizeof(used)); dis[1] = 100; used[1] = 1; q.push(1); in[1]++; while( !q.empty() ) { int x = q.front(); q.pop(); used[x] = 0; if( in[x] > n ) break; for(i=1; i<=n; i++) if( map[x][i] && dis[i] < dis[x] + en[i] ) { dis[i] = dis[x] + en[i]; if( !used[i] ) { in[i]++; used[i] = 1; q.push(i); } } } while( !q.empty() ) q.pop(); if( dis[n] > 0 ) return 1; else { for(i=1; i<=n; i++) for(j=1; j<=n; j++) for(k=1; k<=n; k++) if( map[j][i] && map[i][k] ) map[j][k] = 1; for(i=1; i<=n; i++) if( in[i] > n && map[i][n] && map[1][i] ) return 1; } return 0; } int main() { int j,k,i,m,to,ans; int tmp[110][110]; while( scanf("%d",&n) != EOF && n != -1 ) { memset(map,0,sizeof(map)); for(i=1; i<=n; i++) { scanf("%d%d",&en[i],&m); while(m--) { scanf("%d",&to); map[i][to] = 1; } } ans = SPFA(); if( ans ) printf("winnable\n"); else printf("hopeless\n"); } return 0; }