1.题目描述:点击打开链接
2.解题思路:本题利用BellmanFord算法+二分解决。本题要求执行完一系列Halum操作后,可以让边权的最小值非负且尽量大。自然想到可以用二分法来解决。假设答案是x。即问题转化为所有边的边权经过操作后都大于等于x。然而这里有一个问题,我们根本不知道应该怎么选取相应的v,d,使得可以达到这个目标。但是仔细观察后就可以注意到一个特点:不同的操作影响是相互独立的,即影响是符合叠加原理的。假设我们对u结点执行了d1,d2..dk种操作,可以等价为只执行了d1+d2+..+dk这一种操作。这样,我们可以考虑把每个结点i等效的操作数设置为Xi,那么对于每一条边A->B,有w(A,B)+X(A)-X(B)>=x,移项后得到X(B)-X(A)<=w(A,B)-x。这样,我们实际上得到了m个不等式组。这个不等式组构成了一个“差分约束系统”。如果这个不等式组有解,那么x值就是可以得到的,否则就是不可能得到的。那么现在问题转化为如何判断解是否存在。
考虑到最短路中的不等式d[v]<=d[u]+w[u,v],我们可以用最短路来判断,把0结点处的X(0)设置为0,如果有X(B)-X(A)<=w(A,B)-x,就连一条A->B的边,边权恰好是w(A,B)-x。这样就和最短路的不等式完全一样了。可以直接用Dijkstra算法求解了。但是我们没有必要真的求出来每个点的具体值,而只是判断解是否存在。最短路不存在的条件当且仅当图中存在负圈。那么就可以用BellmanFord算法来判断解的存在性了。至此,问题得以解决。
3.代码:
#include<iostream>
#include<algorithm>
#include<cassert>
#include<string>
#include<sstream>
#include<set>
#include<bitset>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cctype>
#include<list>
#include<complex>
#include<functional>
using namespace std;
#define me(s) memset(s,0,sizeof(s))
#define rep(i,n) for(int i=0;i<(n);i++)
#define pb push_back
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
typedef pair <int,int> P;
const int INF=1e9;
const int maxn=500+10;
const int maxm=2700+10;
struct Edge{int to,dist;};
struct BellmanFord
{
int n,m;
Edge edges[maxm];
int head[maxn];
int next[maxm];
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 u,int v,int dist)
{
edges[m]=Edge{v,dist};
next[m]=head[u];
head[u]=m++;
}
bool negativeCycle()
{
queue<int>q;
memset(inq,0,sizeof(inq));
memset(cnt,0,sizeof(cnt));
for(int i=0;i<n;i++){d[i]=0;q.push(i);}
while(!q.empty())
{
int u=q.front();q.pop();
inq[u]=false;
for(int i=head[u];~i;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])
{
q.push(e.to);inq[e.to]=true;
if(++cnt[e.to]>n)return true;
}
}
}
}
return false;
}
};
BellmanFord solver;
bool test(int x)//判断边权的最小值是否可以为x,等价于判断所有边权都减掉x后,整个图是否存在负圈
{
for(int i=0;i<solver.m;i++)
solver.edges[i].dist-=x;
bool ret=solver.negativeCycle();
for(int i=0;i<solver.m;i++)
solver.edges[i].dist+=x;
return !ret;
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
solver.init(n);
int ub=0;
while(m--)
{
int u,v,d;
scanf("%d%d%d",&u,&v,&d);
ub=max(ub,d);
solver.addedge(u-1,v-1,d);
}
if(test(ub+1))puts("Infinite"); //如果最小值可以达到最大值+1,说明是可以无限大的,因为如果反复执行相同的操作,就会让边权继续增大,趋于无限
else if(!test(1))puts("No Solution");//如果最小值连1都达不到,说明无解
else
{
int L=2,R=ub,ans=1;
while(L<=R)
{
int M=L+(R-L)/2;
if(test(M)){ans=M;L=M+1;}
else R=M-1;
}
printf("%d\n",ans);
}
}
}