题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1317
题意:给一张有向的点权图,一个人在起点有初始值100,这个人从一个点到另一个点时要加上对应目标点的权值。问有没有一条路径使得这个人的当前值都是正数的前提下,到达终点。
由于问能不能,考虑一个情况就是有正数环的情况,假如这个环可以到达终点,那么无论如何都可以到达终点。这个可达的状态可以通过floyd来求传递闭包得出。因为相当于是求最长路,那么负环就不必要考虑了。
所以做法就是先floyd求出传递闭包,在基础上做spfa,更新两个点之间的时候要考虑是否可达。
1 #include <cstdio> 2 #include <queue> 3 #include <vector> 4 #include <cstring> 5 #include <iostream> 6 #include <algorithm> 7 using namespace std; 8 typedef pair<int, int> pii; 9 10 const int maxn = 110; 11 const int maxm = 10100; 12 const int inf = 0x7f7f7f; 13 const int nl = 100; 14 vector<pii> G[maxn]; 15 bool dp[maxn][maxn]; 16 int vcnt[maxn]; 17 int d[maxn]; 18 queue<int> q; 19 int n, m; 20 21 void floyd() { 22 for(int k = 1; k <= n; k++) { 23 for(int i = 1; i <= n; i++) { 24 for(int j = 1; j <= n; j++) { 25 if(dp[i][k] && dp[k][j]) { 26 dp[i][j] = 1; 27 } 28 } 29 } 30 } 31 } 32 33 bool spfa(int s) { 34 memset(vcnt, 0, sizeof(vcnt)); 35 for(int i = 1; i <= n; i++) d[i] = -inf; 36 while(!q.empty()) q.pop(); 37 q.push(s); d[s] = 100; 38 while(!q.empty()) { 39 int u = q.front(); q.pop(); 40 if(vcnt[u] > nl) return dp[u][n]; 41 for(int i = 0; i < G[u].size(); i++) { 42 int v = G[u][i].first, w = G[u][i].second; 43 if(dp[u][v] && d[v] < d[u] + w && d[u] + w > 0) { 44 d[v] = d[u] + w; 45 vcnt[v]++; 46 q.push(v); 47 } 48 } 49 } 50 return d[n] > 0; 51 } 52 53 int main() { 54 // freopen("in", "r", stdin); 55 int v, w, m; 56 while(~scanf("%d",&n) && ~n) { 57 memset(dp, 0, sizeof(dp)); 58 for(int i = 1; i <= n; i++) { 59 G[i].clear(); 60 } 61 for(int u = 1; u <= n; u++) { 62 scanf("%d %d", &w, &m); 63 while(m--) { 64 scanf("%d", &v); 65 dp[u][v] = 1; 66 G[u].push_back(pii(v, w)); 67 } 68 } 69 floyd(); 70 if(dp[1][n]) { 71 if(spfa(1)) puts("winnable"); 72 else puts("hopeless"); 73 } 74 else puts("hopeless"); 75 // for(int i = 1; i <= n; i++) printf("%d ", d[i]); 76 // printf(" "); 77 } 78 return 0; 79 }