POJ 1932 XYZZY (差分约束+传递闭包)

题目链接

题意

N N 个屋子,走进每个屋子血量都会发生改变,开始生命值 100 100 。问是否可以从 1 1 号屋子走到 N N 号屋子中间血量保持大于 0 0

思路

  • 按照给定的顺序建图,因为要让血量尽可能的高所以求最长路,如果走到 N N 的最长路的血量小于0那么就无解。
  • 最长路可能存在正环,当存在正环而且 1 1 N N 联通,那么一定有解
  • 个人认为标程应该是 s p f a spfa 判正环+传递闭包判联通,网上有人是判断存在正环之后把 d i s dis 赋值为 i n f inf 最后判断dis[n] > 0,这样其实不对,把 d i s dis 置为 i n f inf 不一定能更新到 d i s [ n ] > 0 dis[n] > 0 ,可以测下这个数据。
5
0 1 2
1 1 3
2 1 4
3 2 2 5
-1061109567 0
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <vector>
#include <stdio.h>
#include <iostream>
#include <numeric>
#include <algorithm>
#include <cstring>
#include <time.h>
#define LL long long
#define P pair<int, int>
#define lowbit(x) (x & -x)
#define mem(a, b) memset(a, b, sizeof(a))
#define mid ((l + r) >> 1)
#define lc rt<<1
#define rc rt<<1|1
#define endl '\n'
const int maxn = 1e2 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
using namespace std;
vector<int> g[maxn];
int dis[maxn], vis[maxn], cnt[maxn], val[maxn];
int mp[maxn][maxn];
int n;
int Spfa(int s, int e) {
	for (int i = 1; i <= e; ++i) {
		dis[i] = -inf;
	}
	mem(vis, 0);
	mem(cnt, 0);
	vis[s] = 1;
	cnt[s] = 1;
	dis[s] = 100;
	queue<int> que;
	que.push(s);
	while (!que.empty()) {
		int f = que.front();
		que.pop();
		vis[f] = 0;
		if (cnt[f] >= n) dis[f] = inf;
		int len = g[f].size();
		for (int i = 0; i < len; ++i) {
			int t = g[f][i];
			if (dis[t] < dis[f] + val[t] && dis[f] + val[t] > 0) {
				dis[t] = dis[f] + val[t];
				if (vis[t] == 0 && ++cnt[t] <= n) {
					vis[t] = 1;
					que.push(t);
				}
				if (cnt[t] > e) return -1;
			}
		}
	}
	return dis[e] > 0;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    while (scanf("%d", &n) != EOF) {
    	mem(mp, 0);
    	if (n == -1) break;
    	for (int i = 0; i <= n; ++i) g[i].clear();
    	int num, d;
    	for (int i = 1; i <= n; ++i) {
    		scanf("%d%d", &val[i], &num);
    		for (int j = 0; j < num; ++j) {
    			scanf("%d", &d);
    			g[i].push_back(d);
    			mp[i][d] = 1;
    		}
    	}
    	int ans = Spfa(1, n);
    	if (ans == -1){
    		for (int k = 1; k <= n; ++k) {
    			for (int i = 1; i <= n; ++i) {
    				for (int j = 1; j <= n; ++j) {
    					if (mp[i][k] && mp[k][j]) mp[i][j] = 1;
    				}
    			}
    		}
    		for (int i = 1; i <= n; ++i) {
    			if (mp[1][i] && mp[i][n] && cnt[i] > n) {
    				ans = 1;
    				break;
    			}
    		}
    	}
    	if (ans == 1) puts("winnable");
    	else puts("hopeless");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/henuyh/article/details/89493559