题目链接:
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=34651
题意:
给定一个有向图,每一条边都有一个权值,每次你可以选择一个节点v和一个整数d,把所有以v结尾的边权值减小d,把所有以v为起点的边的权值增加d,最后要让所有边权的最小值大于0且尽量大。
题解:
最小值最大,可以用二分,这样可以得到一个差分约束系统,然后每次都用最短路跑。
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> using namespace std; const int maxn = 555; const int maxm = 2777; struct Edge { int v, w; Edge(int v, int w) :v(v), w(w) {} Edge() {} }; vector<Edge> egs; vector<int> G[maxn]; int tot = 0; int n, m; void addEdge(int u, int v, int w) { egs.push_back(Edge(v, w)); tot = egs.size(); G[u].push_back(tot - 1); } bool inq[maxn]; int d[maxn]; int cnt[maxn]; bool spfa(int x) { bool ret = true; for (int i = 1; i < n; i++) { for (int j = 0; j < G[i].size(); j++) { Edge& e = egs[G[i][j]]; e.w -= x; } } memset(inq, 0, sizeof(inq)); memset(cnt, 0, sizeof(cnt)); memset(d, 0x3f, sizeof(d)); queue<int> Q; d[0] = 0; inq[0] = true; Q.push(0); while (!Q.empty()) { int u = Q.front(); Q.pop(); inq[u] = false; for (int i = 0; i < G[u].size(); i++) { Edge& e = egs[G[u][i]]; if (d[e.v] > d[u] + e.w) { d[e.v] = d[u] + e.w; if (!inq[e.v]) { Q.push(e.v); inq[e.v] = true; if (++cnt[e.v] > n) { ret = false; break; } } } } if (ret == false) break; //printf("u:%d\nx:%d\n", u,x); //printf("in circle\n"); } for (int i = 1; i < n; i++) { for (int j = 0; j < G[i].size(); j++) { Edge& e = egs[G[i][j]]; e.w += x; } } return ret; } void init() { for (int i = 0; i < n; i++) G[i].clear(); egs.clear(); } int main() { while (scanf("%d%d", &n, &m) == 2 && n) { n++; init(); int l = 1, r = -1; for (int i = 0; i < m; i++) { int u, v, w; scanf("%d%d%d", &u, &v, &w); r = max(r, w); addEdge(u, v, w); } for (int i = 1; i < n; i++) { addEdge(0, i, 0); } r++; if (!spfa(1)) { printf("No Solution\n"); } else if (spfa(r)) { printf("Infinite\n"); } else { while (l + 1 < r) { int mid = l + (r - l) / 2; if (spfa(mid)) l = mid; else r = mid; //printf("here!\n"); } printf("%d\n", l); } } return 0; } /* 1 2 10 1 2 -10 3 2 4 3 2 1 5 5 3 4 2 5 4 2 1 0 2 -1 */