Uva 11478 - Halum(二分+差分约束)
题目链接 https://vjudge.net/problem/UVA-11478
【题意】
给定一张带权有向图,每次你可以选择一个结点v和一个整数d,把所有以v为终点的边的权值减少d,把所有以v为起点的边的权值增加d,最后要让所有边权最小值大于0且尽量大。对于每组数据输出边权最小值的最大值,如果无法让所有边权都大于0则输出”No Solution”,如果边权最小值可任意大,输出”Infinite”
【思路】
二分+差分约束系统,首先最小值最大让我们想到了二分答案,将问题转换为是否在所有操作完成后使得所有边的权值大于等于x,假设sum(u)为作用在结点u上的所有d之和,那么对于每条边(u,v),在操作完成后的权值就变成了w(u,v)+sum(u)-sum(v),要想满足假设的话则w(u,v)+sum(u)-sum(v)>=x,移项即sum(v)-sum(u)<=w(u,v)-x,对于每条边都是这样,于是构成了一个查分约束系统,利用BellmanFord来判断是否有解,有解则猜测的x成立,否则不成立。
#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 550;
struct Edge {
int from, to, dist;
Edge(int f, int t, int d) :from(f), to(t), dist(d) {}
};
struct BellmanFord {
int n, m;
vector<Edge> edges;
vector<int> g[maxn];
bool inq[maxn];
int d[maxn];
int p[maxn];
int cnt[maxn];
void init(int n) {
this->n = n;
for (int i = 0; i < n; ++i) g[i].clear();
edges.clear();
}
void add(int from, int to, int dist) {
edges.push_back(Edge(from, to, dist));
m = edges.size();
g[from].push_back(m - 1);
}
bool negativeCycle() {
queue<int> que;
memset(inq, 0, sizeof(inq));
memset(cnt, 0, sizeof(cnt));
for (int i = 0; i < n; ++i) { d[i] = 0; inq[i] = true; que.push(i); }
while (!que.empty()) {
int u = que.front();
que.pop();
inq[u] = false;
for (int i = 0; i < g[u].size(); ++i) {
Edge& e = edges[g[u][i]];
if (d[e.to] > d[u] + e.dist) {
d[e.to] = d[u] + e.dist;
p[e.to] = g[u][i];
if (!inq[e.to]) {
que.push(e.to);
inq[e.to] = true;
if (++cnt[e.to] > n) return true;
}
}
}
}
return false;
}
};
int n, m;
BellmanFord solver;
bool check(int x) {
for (int i = 0; i < solver.m; ++i) {
solver.edges[i].dist -= x;
}
bool ans = !solver.negativeCycle();
for (int i = 0; i < solver.m; ++i) {
solver.edges[i].dist += x;
}
return ans;
}
int main() {
while (scanf("%d%d", &n, &m) == 2) {
solver.init(n);
int le = 1, ri = 0, mid;
for (int i = 0; i < m; ++i) {
int u, v, c;
scanf("%d%d%d", &u, &v, &c);
--u, --v;
ri = max(ri, c);
solver.add(u, v, c);
}
if (check(ri)) {
printf("Infinite\n");
continue;
}
if (!check(le)) {
printf("No Solution\n");
continue;
}
while (le + 1 < ri) {
mid = (le + ri) >> 1;
if (check(mid)) le = mid;
else ri = mid;
}
printf("%d\n", le);
}
return 0;
}