诶,,还是第一次处理负环
先给一组数据
6 0 2 2 5 1 1 3 1 1 4 1 1 2 -100 1 6 0 0首先第一步肯定是要处理能量,把能量全部取负,这样就能套用最短路了
我刚开始的想法是,如果发现负环,就看起点和终点是否连通,如果连通,那么就能够到达,,因为总可以通过负环,把能量弄到无穷大
但是却没有仔细考虑,如果那个负环不是在起点和终点的路上,换句话说,就是负环和终点可能并不连通~,所以跪了
后来改了一下,,是这样想的
先找负环,然后确定负环上面的一个点,然后查看这个点是否和终点连通,如果连通说明肯定能通过负环累积能量到无穷大然后再到达终点,肯定能胜利
如果不能到达终点,就删掉这个点,这样就构成不了负环了,然后再做spfa,直到发现一个可以到达终点的负环,或者是负环全部删除了为止
#include<cstdio> #include<cmath> #include<cstring> #include<queue> #include<vector> #include<map> #include<string> #include<iostream> #include<functional> #include<algorithm> using namespace std; typedef long long LL; typedef pair<int, int> PII; const int MX = 20000 + 5; const int INF = 0x3f3f3f3f; int Head[MX], Next[MX], rear; int energy[MX], vis[MX], d[MX], del[MX]; struct Edge { int u, v; } E[MX]; void edge_init() { rear = 0; memset(Head, -1, sizeof(Head)); } void edge_add(int u, int v) { E[rear].u = u; E[rear].v = v; Next[rear] = Head[u]; Head[u] = rear++; } int spfa_dfs(int u) { vis[u] = 1; for(int i = Head[u]; ~i; i = Next[i]) { int v = E[i].v, w = energy[v], t; if(del[v]) continue; if(d[u] + w < d[v] && d[u] + w < 0) { d[v] = d[u] + w; if(!vis[v]) { if(t = spfa_dfs(v)) { vis[u] = 0; return t; } } else { vis[u] = 0; return v; } } } vis[u] = 0; return 0; } bool connect(int Begin, int End) { if(Begin == End) return true; vis[Begin] = 1; for(int i = Head[Begin]; ~i; i = Next[i]) { int v = E[i].v; if(!vis[v] && connect(v, End)) { return true; } } return false; } int main() { int n, m; while(~scanf("%d", &n), n >= 0) { edge_init(); memset(del, 0, sizeof(del)); for(int i = 1; i <= n; i++) { scanf("%d%d", &energy[i], &m); energy[i] = -energy[i]; for(int j = 1; j <= m; j++) { int v; scanf("%d", &v); edge_add(i, v); } } bool sign = false; for(;;) { memset(d, INF, sizeof(d)); memset(vis, 0, sizeof(vis)); d[1] = -100; int ret = spfa_dfs(1); if(!ret) break; if(connect(ret, n)) { sign = true; break; } del[ret] = 1; } if(sign) printf("winnable\n"); else printf("%s\n", d[n] >= 0 ? "hopeless" : "winnable"); } return 0; }