题目链接:https://cn.vjudge.net/contest/315275#problem/F
Sample Input
5
0 1 2
-60 1 3
-60 1 4
20 1 5
0 0
5
0 1 2
20 1 3
-60 1 4
-60 1 5
0 0
5
0 1 2
21 1 3
-60 1 4
-60 1 5
0 0
5
0 1 2
20 2 1 3
-60 1 4
-60 1 5
0 0
-1
Sample Output
hopeless
hopeless
winnable
winnable
翻译:
初始有100能量,每个房间都有一个能量值,走到这个房间可以获得(能量值可以有负)。判断从1能否走到n,每走一步都必须保证能量大于0,小于等于0就不能走。
输入一个n,表示有n个房间,每行第一个数表示房间i的能量值。第二个数表示与房间i相连的房间。
分析:每走一步都必须保证能量大于0,最短路肯定不行。第一思路直接改模板变成最长路。要明白你求得的各个顶点的最长路与题目中的描述完全是两个不同的概念。题目要求从1到n走到各个顶点都必须保证自身的能量大于0。可以想到是借助判断是否有正权回路的思想来写(实则从1到n没有成回路,还少一条边)。
1.借助Floyd判断各点的连同情况。
2.SPFA判断各点入队列的次数来判断回路。入队列必须保证:这两点是连同的,走到这一点的获得的能量加上原来的能量必须大于0和原来的能量。(保证每走一步获得的能量都是大于0的,正权回路不能有负数,不然与正权回路相矛盾)。
代码:
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
int e[102][102],a[102][102],pow1[102];
int dis[102],time[102],book[102];
int n;
void Floyd() { /*判断与n相连的点*/
for(int k=1; k<=n; k++)
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
if(a[i][k]&&a[k][j])
a[i][j]=1;
}
int SPFA() {
memset(dis,0,sizeof(dis));
memset(time,0,sizeof(time));
memset(book,0,sizeof(book));
dis[1]=100;
queue<int>q;
q.push(1);
book[1]=1;
while(!q.empty()) {
int u=q.front();
time[u]++;
q.pop();
if(time[u]>=n)/*判断成一个正环*/
return a[u][n];
for(int i=1; i<=n; i++) {
if(e[u][i]&&dis[i]<dis[u]+pow1[i]&&dis[u]+pow1[i]>0) { /*两点必须连通,走到下一个房间pow大于0,*/
dis[i]=dis[u]+pow1[i];
q.push(i);
}
}
}
if(dis[n]>0)
return 1;
else
return 0;
}
int main() {
while(~scanf("%d",&n)) {
if(n==-1)break;
memset(e,0,sizeof(e));
memset(a,0,sizeof(a));
for(int i=1; i<=n; i++) { /*n个房间*/
int m;
scanf("%d%d",&pow1[i],&m);/*第i个房间拥有的能量值*/
while(m--) {
int a1;/*与i相连的房间有a1个*/
scanf("%d",&a1);
e[i][a1]=1;
a[i][a1]=1;
}
}
Floyd();
if(!a[1][n])
printf("hopeless\n");
else {
if(SPFA())
printf("winnable\n");
else
printf("hopeless\n");
}
}
return 0;
}