题目中给了一种操作,就是制定一个d,和一个结点v,把所有以v为终点的边的权值减少d,把所有以v为起点的边权增加d,然后使最小的边权最大。
看到最小最大这类的字眼,首先想到的就是二分答案,假设每一条边的边权都不小于x,对于一条边,是从a指向b的,然后假定sum(a)和sum(b)分别是左右于a和b上的所有操作的d的和,那么对于这条边,它的权重就应该是w+sum(a)-sum(b)>=x,整理一下就可以得到sum(b)-sum(a)<=w-x,这样就得到了一个差分约束系统了,如果它是有解的,那么二分到的x就是合理的,如果无解,就只能说x大了。
有两种情况需要特判,如果x是最大边权,差分约束系统依然有解,就说明最小边权可以任意大,如果x是1,差分约束系统就无解了,就说明找不到一个最小最大非负边权。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
const int maxn = 500 + 10;
const int maxm = 2700 + 10;
const int INF = 0x7fffffff;
struct Edge{
int to, dist;
};
struct SPFA{
int n, m;
Edge edges[maxm];
int head[maxn], next[maxn];
bool inq[maxn];
int d[maxn], cnt[maxn];
void init(int n){
this -> n = n;
m = 0;
memset(head, -1, sizeof(head));
}
void AddEdge(int from, int to, int dist){
next[m] = head[from];
head[from] = m;
edges[m++] = (Edge){to, dist};
}
bool spfa(){
queue<int> que;
memset(inq, 0, sizeof(inq));
memset(cnt, 0, sizeof(cnt));
for (int i = 0; i < n; i++){
d[i] = 0; que.push(i);
}
while(!que.empty()){
int u = que.front(); que.pop();
inq[u] = 0;
for (int i = head[u]; i != -1; i = next[i]){
Edge& e = edges[i];
if (d[e.to] > d[u] + e.dist){
d[e.to] = d[u] + e.dist;
if (!inq[e.to]){
inq[e.to] = 1; que.push(e.to);
if (++cnt[e.to] > n) return 1;
}
}
}
}
return 0;
}
};
SPFA solver;
bool test(int x){
for (int i = 0; i < solver.m; i++)
solver.edges[i].dist -= x;
bool ret = solver.spfa();
for (int i = 0; i < solver.m; i++)
solver.edges[i].dist += x;
return !ret;
}
int main(){
// freopen("in.txt", "r", stdin);
int n, m;
while(~scanf("%d%d", &n, &m)){
solver.init(n);
int ub = 0;
int x, y, z;
while(m--){
scanf("%d%d%d", &x, &y, &z);
ub = max(z, ub);
x -= 1; y -= 1;
solver.AddEdge(x, y, z);
}
if (test(ub + 1)) puts("Infinite");
else if (!test(1)) puts("No solution");
else{
int L = 2, R = ub, ans = 1;
while(L <= R){
int M = L + ((R - L) >> 1);
if (test(M)){ans = M; L = M + 1;}
else R = M - 1;
}
printf("%d\n", ans);
}
}
return 0;
}