题意:给定一个有向带权图,每次可以选择一个节点v和一个整数d,使得所有以v为终点的边权减少d,所有以v为起点的边权值增加d,求所有边权中最小值的最大值,如果可以无限大输出 Infinite,如果不是正数输出No Solution。
思路:可以二分这个“最小值最大”,二分一个答案x,设sum[ a ]为以a为起点的边增加的值,那么对于a->b,可以得到 dist[ a ][ b ]+sum[ a ]-sum[ b ]>=x,进一步得到sum[ b ]-sum[ a ]<=dist[ a ][ b ]-x,因此问题转化成了差分约束看不等式组是否有解,将a连接b,设边权为dist[ a ][ b ]-x,得到一个新的图,如果该图有负环,则无解,可用贝尔曼弗洛伊德算法判负环。
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=500+5;
const int inf=1e8;
vector<int>G[maxn],dis[maxn],dis1[maxn];
int n,inq[maxn],d[maxn],cnt[maxn];
void init()
{
for(int i=0;i<=n;i++)
{
G[i].clear();
dis[i].clear();
}
}
int ford()
{
queue<int>q;
for(int i=0;i<n;i++)
{
d[i]=cnt[i]=0;
inq[i]=1;
q.push(i);
}
while(!q.empty())
{
int u=q.front();q.pop();
inq[u]=0;
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i];
if(d[v]>d[u]+dis1[u][i])
{
d[v]=d[u]+dis1[u][i];
if(!inq[v])
{
q.push(v);
inq[v]=1;
if(++cnt[v]>n)
return true;
}
}
}
}
return false;
}
int work(int k)
{
for(int i=0;i<n;i++)
dis1[i].clear();
for(int i=0;i<n;i++)
for(int j=0;j<G[i].size();j++)
dis1[i].push_back(dis[i][j]-k);
return ford();
}
int main()
{
int m,i,u,v,dist;
while(~scanf("%d%d",&n,&m))
{
init();
int L=1,R=0,M;
for(i=0;i<m;i++)
{
scanf("%d%d%d",&u,&v,&dist);u--;v--;
G[u].push_back(v);
dis[u].push_back(dist);
R=max(R,dist);
}
if(!work(R))
printf("Infinite\n");
else if(work(1))
printf("No Solution\n");
else
{
while(L<R)
{
M=(L+R)/2;
if(!work(M))
{
if(L==M)
break;
L=M;
}
else
R=M;
}
printf("%d\n",M);
}
}
}