UVA_10557
这个题目实际上是求一个从起点到终点的最长路。
但是由于路过每个点时权值都不能为负,所以我们在初始化距离数组d[]时要初始化0。之后就是用队列优化的Bellman-Ford算法去求最长路了,但是还有一些细节需要注意。
我们当然可以在用这个算法时选择d[n]>0是就跳出循环来减少计算量,但也会找到使这个算法崩溃的例子,比如在某个位置存在一个正圈,而由这个正圈却不能到达终点,那么程序就会一直跑下去。但同样的,我们也不能见圈就跳,因为毕竟还是有些正圈可以到达终点的。这便要求我们去判断哪些正圈可以到达终点。
于是我们可以选择在找最长路之前先做一下预处理,把所有能够到达终点的点找出来,一个比较好的办法就是从终点开始逆向深搜,然后依次标记搜到的点即可,如果最后起点没有被标记,那就直接输出hopeless就可以了。
后面在使用队列优化的Bellman-Ford算法时,我们在判断条件上多加一个是否可达终点的判断即可,如果该点可达终点我们再进行求最长路的操作。
#include<stdio.h>
#include<string.h>
int G[110][110],w[110],d[110],n;
int q[110],inq[110],inedq[110];
int vis[110],reach[110];
void dfs(int v)
{
int u;
for(u=1;u<=n;u++)
if(G[u][v]&&!vis[u])
{
vis[u]=1;
reach[u]=1;
dfs(u);
}
}
int main()
{
int i,j,k,u,v,num,front,rear,flag;
while(1)
{
scanf("%d",&n);
if(n==-1)
break;
memset(G,0,sizeof(G));
for(u=1;u<=n;u++)
{
scanf("%d",&w[u]);
scanf("%d",&num);
for(i=0;i<num;i++)
{
scanf("%d",&v);
G[u][v]=1;
}
}
memset(vis,0,sizeof(vis));
memset(reach,0,sizeof(reach));
reach[n]=1;
dfs(n);
if(!reach[1])
{
printf("hopeless\n");
continue;
}
memset(inq,0,sizeof(inq));
memset(inedq,0,sizeof(inedq));
memset(d,0,sizeof(d));
front=rear=0;
d[1]=100;
q[rear++]=1;
inq[1]=1;
inedq[1]++;
flag=0;
while(front!=rear)
{
u=q[front++];
if(front>n)
front=0;
inq[u]=0;
for(v=1;v<=n;v++)
if(G[u][v]&&reach[v]&&d[u]+w[v]>d[v])
{
d[v]=d[u]+w[v];
if(!inq[v])
{
q[rear++]=v;
if(rear>n)
rear=0;
inq[v]=1;
if(inedq[v]++>n)
{
flag=1;
break;
}
}
}
if(d[n]>0||flag)
break;
}
if(d[n]>0||flag)
printf("winnable\n");
else
printf("hopeless\n");
}
return 0;
}