Description
Each game consists of a set of up to 100 rooms. One of the rooms is the start and one of the rooms is the finish. Each room has an energy value between -100 and +100. One-way doorways interconnect pairs of rooms.
The player begins in the start room with 100 energy points. She may pass through any doorway that connects the room she is in to another room, thus entering the other room. The energy value of this room is added to the player's energy. This process continues until she wins by entering the finish room or dies by running out of energy (or quits in frustration). During her adventure the player may enter the same room several times, receiving its energy each time.
Input
the energy value for room i
the number of doorways leaving room i
a list of the rooms that are reachable by the doorways leaving room i
The start and finish rooms will always have enery level 0. A line containing -1 follows the last test case.
Output
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
综合题,看别人的代码过的。输入也太TM恶心了。有一个疑惑,为什么不用vis数组标记是否入队过也能过,那么问题来了,什么时候必须用vis数组标记??
上代码~~
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <vector> 6 #include <queue> 7 #include <set> 8 #include <map> 9 #include <string> 10 #include <cmath> 11 #include <stdlib.h> 12 #define N 1005 13 using namespace std; 14 15 bool dis[N][N]; 16 int n,i,j,a,b,c,d[N],cnt[N],vis[N]; 17 struct Edge{ 18 int u,v,w; 19 }; 20 vector<Edge> edge; 21 vector<int> g[N]; 22 void init() 23 { 24 for(i = 0;i<=n;i++) 25 g[i].clear(); 26 edge.clear(); 27 } 28 void add(int u,int v,int w) 29 { 30 Edge t; 31 t.u = u; t.v = v; t.w = w; 32 edge.push_back(t); 33 g[u].push_back(edge.size()-1); 34 } 35 void floyd() 36 { 37 int k; 38 for(k = 1;k<=n;k++) 39 for(i= 1;i<=n;i++) 40 for(j = 1;j<=n;j++) 41 dis[i][j] |= (dis[i][k]&&dis[k][j]); 42 } 43 int spfa() 44 { 45 queue<int> q; 46 q.push(1); 47 memset(cnt,0,sizeof(cnt)); 48 memset(d,0,sizeof(d)); 49 memset(vis,0,sizeof(vis)); 50 vis[1] = 1; 51 d[1] = 100; 52 while(!q.empty()) 53 { 54 int now = q.front(); 55 q.pop(); 56 cnt[now]++; 57 vis[now] = 0;////////// 58 if(cnt[now]>=n) return dis[now][n]; 59 for(i = 0;i<g[now].size();i++) 60 { 61 Edge e = edge[g[now][i]]; 62 if(d[e.u]+e.w>d[e.v]&&d[e.u]+e.w>0) 63 { 64 d[e.v] = d[e.u]+e.w; 65 if(vis[e.v]==0) 66 { 67 q.push(e.v); 68 vis[e.v] = 1; 69 } 70 } 71 } 72 } 73 return d[n]>0; 74 } 75 int main() 76 { 77 freopen("caicai.txt","r",stdin); 78 while(scanf("%d",&n)!=EOF) 79 { 80 if(n==-1) 81 break; 82 memset(dis,0,sizeof(dis)); 83 init(); 84 for(i = 1;i<=n;i++) 85 { 86 scanf("%d%d",&a,&b); 87 for(j = 1;j<=b;j++) 88 { 89 scanf("%d",&c); 90 add(i,c,a); 91 dis[i][c] = 1; 92 } 93 } 94 floyd(); 95 if(!dis[1][n]) 96 printf("hopeless "); 97 else{ 98 int ans = spfa(); 99 if(ans) 100 printf("winnable "); 101 else 102 printf("hopeless "); 103 } 104 } 105 return 0; 106 }
题解转自http://blog.csdn.net/u013445530/article/details/44066325
题意: 有n个房间(n<=100),每个房间有一个点权(第1号房间和第n号房间权值均为0),到达该房间时会自动获得该点权(可能为负权)。给出一些无 向边。有一个人,初始有能量值100,初始位置是第1号房间,要走到第n号房间,且路途中不得使身上能量值小于或等于0。能到达第n个房间就算赢,问能否 赢。
思路:
1.首先可以发现,只要知道到第n个房间时最大可以获得多少能量值(当然必须保证中途都大于0),就能知道能否赢。
2.考虑到负环情况,跑SPFA的时候最长路负环是不会去循环的,所以负环其实可以不用考虑。
3.考虑到正环情况,只要能到达正环(即SPFA处理过程中接触到正环中的点),并且正环能到达终点,就一定能赢。
4.SPFA最长路松弛操作只要把松弛条件里的大于号小于号什么的反一下就可以了。
5.由于SPFA入队的点一定是能够由源点到达的点,而如果一个点进队次数大于等于n就说明 它在环中(事实上由于最长路中负环不会循环所以这里一定是正环),那么这里只要能从该点到达终点(Floyd判断),那么就一定能赢;反之如果不能到达终 点就一定会输(因为松弛次数大于或等于n还没有结束则说明最长路不存在)。
6.点权的写法,d数组的含义是由源点s到当前点u(包含u)的路径上的点权之和,所以初始化d[s]=100.
7.SPFA中松弛要求原图的连通情况,而不能用Floyd出来的结果。
8.注意松弛条件中本题要求中途都不能<=0因此松弛优化要满足优化值>0,于是要加个d[u]+E[v]>0。
9.注意最后判断一下到达第n号房间时的能量值是否>0,这个数值即为到达第n个房间时最大可以获得多少能量值。
10.程序中map数组用来存放原图,用来SPFA中的松弛条件;reach数组用来判断u->v的连通性。
11.注意Floyd是这样写的reach[i][j]=reach[i][j] || (reach[i][k] && reach[k][j]);
12.注意输入中可到达的房间编号是以1开始的,所以程序中想从0开始编号要将输入数值减1(作死无数次……)。