首先要读懂题,做这个题时读题看样例琢磨了半天才明白= =;
题目的意思是,一个人从一号房间走到最后一个房间,每个房间有相应的能量值(可正可负),当走到这个房间的时候可以获取这个房间的能量值,如果走到最后一个房间时,人的能量值为正,则“winnable"”,否则的话就"hopeless",另外,这个人一开始自身有100点能量值,房间可以重复的走,且第一个房间和最后一个房间的能量值为零。
首先输入房间数N,然后有n行,每行输入i,j三个数,i代表这个房间所拥有的能量值,j代表这个房间一共可以走向其他的房间的个数,其次有j个数,代表可以走向哪几个房间。
解题思路:
首先用floyd遍历一下,标记那些点可以走向最后一个房间,如果从第一个无法走向最后一个,直接输出"hopeless";
其次,用bellman求最短路得方法改写成求最长路,求完后如果dis[n]是个负值,也就是人最多获取的能量还是个负值,就输出"hopeless";
另外,如果图中含有正权环,则一定能winnable,因为房间可以重复走,人可以重复回环走这个正权环来获取能量;但是需要判断一下如果有正权环了,这个环中所含的房间是否有一个能通向最后一个,如果有,才能下结论winnable。
代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct
{
int x,y;
} Edge;
int f[110][110],p[110];
Edge e[11000];
int floyd(int n)
{
int i,j,k;
for(k=1; k<=n; k++)
for(i=1; i<=n; i++)
for(j=1; j<=n; j++)
if(f[i][k]&&f[k][j])
f[i][j]=1;
if(f[1][n]==0)
return 0;
return 1;
}
int bellman(int n,int m)
{
int dis[110];
int inf=-9999999;
int i,k;
for(i=1; i<=n; i++)
dis[i]=inf;
dis[1]=100;
for(k=1; k<n; k++)
for(i=1; i<m; i++)//这里一定要注意,是i<m,没有等号,因为主函数中的m在结束循环时又自增了一,所以不需要加等号,一定要注意
{
if((dis[e[i].x]+p[e[i].y]>0)&&(dis[e[i].y]<dis[e[i].x]+p[e[i].y]))
dis[e[i].y]=dis[e[i].x]+p[e[i].y];
}
for(i=1; i<m; i++)//判断正权环 ,记得m,弱在做的时候因为加了等号错了n遍T-T
{
if((dis[e[i].x]+p[e[i].y]>0)&&(dis[e[i].y]<dis[e[i].x]+p[e[i].y])&&f[e[i].y][n])
return 1;
}
if(dis[n]>0)
return 1;
else
return 0;
}
int main()
{
int i,j,n,m,k;
while(scanf("%d",&n),n!=-1)
{
m=1;
memset(f,0,sizeof(f));
for(i=1; i<=n; i++)
{
scanf("%d%d",&p[i],&j);
while(j--)
{
scanf("%d",&k);
f[i][k]=1;
e[m].x=i;
e[m].y=k;
m++;
}
}
if(floyd(n)==0)
printf("hopeless\n");
else
{
if(bellman(n,m))
printf("winnable\n");
else
printf("hopeless\n");
}
}
return 0;
}