void-man

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

从起点房间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;
}
posted on 2011-04-20 23:46  void-man  阅读(327)  评论(0)    收藏  举报