思路:SPFA+dfs
分析:
1 题目的图是一个有向图,并且可能存在环。第一个点的能量值为100,边的权值利用能量大小,例如2点为-60,如果1->2那么value[1][2] = -60
2 题目明确指出如果是要win的话,那么必须是经过的每条边都要大于0.那么我们只要把那些经过松弛操作后的点大于0的入队即可,小于等于0的点肯定不会出现在最终的路径上。
3 如果存在正环的话,那么就有能量值无限大,那么这个时候只要判断这个点能否到达n
4 判断是否是有环还是五环,用一个s标记,s初始化为0,有环的时候另s = i,然后return。
5 判断当前点s能否到n直接利用dfs即可。
代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<queue> using namespace std; #define MAXN 110 #define INF 0xFFFFFFF int n , s , flag; int value[MAXN][MAXN]; int map[MAXN][MAXN]; int num[MAXN][MAXN]; int energy[MAXN]; int dis[MAXN]; int vis[MAXN]; queue<int>q; /*dfs一遍判断当前点能否到达终点*/ void DFS(int u){ if(u == n){ flag = 1; return; } for(int i = 1 ; i <= n ; i++){ if(map[u][i] && !vis[i]){ vis[u] = 1; DFS(i); } } } /*SPFA找是否有正环*/ void SPFA(){ while(!q.empty()) q.pop(); memset(dis , 0 , sizeof(dis)); memset(vis , 0 , sizeof(vis)); vis[1] = 1; dis[1] = 100;/*初始化为100*/ q.push(1); s = 0; while(!q.empty()){ int x = q.front(); q.pop(); vis[x] = 0; for(int i = 1 ; i <= n ; i++){ if(map[x][i]){ if(dis[i] < dis[x] + value[x][i]){ num[x][i]++; dis[i] = dis[x] + value[x][i]; if(num[x][i] >= n){/*判断是否存在环*/ s = i; return; } if(!vis[i] && dis[i] > 0){/*dis[i]>0只让正权值的边入对列*/ vis[i] = 1; q.push(i); } } } } } } /*输入函数*/ void input(){ int i , j , a , b; memset(value , 0 , sizeof(value)); memset(map , 0 , sizeof(map)); memset(num , 0 , sizeof(num)); for(i = 1 ; i <= n ; i++){ scanf("%d%d" , &energy[i] , &a); for(j = 0 ; j < a ; j++){ scanf("%d" , &b); map[i][b] = 1; } } for(int i = 1 ; i <= n ; i++){ for(int j = 1 ; j <= n ; j++){ if(map[i][j]) value[i][j] = energy[j]; } } } /*输出函数*/ void output(){ if(!s){/*这里如果s为0则肯定没有环*/ flag = 1; if(dis[n] <= 0) flag = 0; } else{ flag = 0; memset(vis , 0 , sizeof(vis)); DFS(s); } if(flag) printf("winnable\n"); else printf("hopeless\n"); } int main(){ while(scanf("%d" , &n) && n != -1){ input(); SPFA(); output(); } return 0; }