差分约束,算法导论上有一节介绍的很详细。
下面来说一下这题是怎样转化成差分约束的。
题目要求每条边权值为正,并且是最小边权最大。一般这种求最小值最大的题目都是用二分。我们二分最小权值x,对每个点所有的操作记为sum()。那么所有操作结束后我们可以得到对于一条边 a->b 有w(a,b) + sum(a) - sum(b) >= x,移向得 sum(b) - sum(a) <= w(a,b) - x。
对于最短路我们有三角不等式:d[b] <= d[a] + w(a,b),移向后 d[b] - d[a] <= w(a,b)。这和上面的式子很像,所以我们可以用最短路来求解差分约束。如果图中不存在负圈则差分约束存在可行解(这个算法导论上有证明,反证法)。我们设一个虚节点0,并从虚节点到其他每一个节点连一条边(权值可以是任意的,因为每个可行解是可以差一个常数的),然后求出每个点关于虚节点的最短路即可。
#include <iostream>
#include<vector>
#include<cstring>
#include<stdio.h>
#include<queue>
#define maxn 510
#define INF 100000
using namespace std;
int n,m;
struct Edge
{
int from,to,dist,next;
};
Edge edges[INF];
int head[maxn];
int vis[maxn];
bool inq[maxn];
int d[maxn];
int p[maxn];
int cnt[maxn];
int ec;
void init()
{
ec = 0;
memset(head,-1,sizeof head);
}
void add_edge(int from,int to,int dist)
{
edges[ec].dist = dist;
edges[ec].from = from;
edges[ec].to = to;
edges[ec].next = head[from];
head[from] = ec;
ec++;
}
bool negative(int mid)
{
queue<int>Q;
memset(inq,0,sizeof inq);
memset(cnt,0,sizeof cnt);
for(int i = 0; i <= n; i++)
{
d[i] = INF;
inq[i] = 1;
Q.push(i);
}
d[0] = 0;
while(!Q.empty())
{
int u = Q.front();
Q.pop();
inq[u] = 0;
for(int i = head[u]; i != -1; i = edges[i].next)
{
Edge& e = edges[i];
if(d[e.to] > d[u] + e.dist)
{
d[e.to] = d[u] + e.dist;
if(!inq[e.to])
{
Q.push(e.to);
inq[e.to] = 1;
if(++cnt[e.to] > n + 1) return true;
}
}
}
}
return false;
}
bool solve(int mid)
{
for(int i = 0; i < ec; i++)
{
Edge& e = edges[i];
if(e.from != 0) e.dist-=mid;
}
bool tmp = negative(mid);
for(int i = 0; i < ec; i++)
{
Edge& e = edges[i];
if(e.from !=0 ) e.dist+=mid;
}
return tmp;
}
int main()
{
while(scanf("%d %d",&n,&m)!=EOF)
{
int low = 0;
int high = -100010;
init();
for(int i = 1; i <= m; i++)
{
int from,to,dist;
scanf("%d %d %d",&from,&to,&dist);
add_edge(from,to,dist);
if(dist > high) high = dist;
}
for(int i = 1; i <= n; i++)
{
add_edge(0,i,-100); //权值可以任意
}
if(!solve(high + 1)) printf("Infinite\n");
else if(solve(1)) printf("No Solution\n");
else
{
int ans = 1;
while(low <= high)
{
int mid = low + (high - low)/2;
if(!solve(mid))
{
ans = mid;
low = mid + 1;
}
else high = mid - 1;
}
printf("%d\n",ans);
}
}
return 0;
}