题意:一个有向图,边权可正可负。对每个顶点,定义H(d)操作:入边边权全部-d,出边边权全部+d,问是否存在若干操作H(d)后,所有边权>=0,若不存在,输出“No Solution”,若存在,求最终最小边的最大值,如果最小边权的最大值可以任意大,输出”Infinite"
二分答案。题目转化为是否存在若干H操作,使得每条边的权值都>=x 。设对每个顶点u的H操作叠加后,为sum(u),则每条边<a,b>都有( sum(a)-sum(b)+w<a,b> )>=x ,转化为不等式组是否有解的问题,可用差分约束系统解决。
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
#include<vector>
#include<queue>
#define ll long long
#define sf scanf
#define pf printf
#define INF 1<<29
#define maxn 100010
#define mem(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x&(-x)
#define maxn 510
const ll mol=1000000007;
using namespace std;
int n,m,maxx;
struct node{
int u,v,w;
}bian[maxn*maxn];
struct Edge{
int to,w,next;
}edge[maxn*maxn];
int head[maxn],tot;
int inq[maxn],cnt[maxn],d[maxn];//是否在队列中&&进队次数&&最短路
void add(int u,int v,int w){
edge[tot].to=v,edge[tot].next=head[u],edge[tot].w=w;
head[u]=tot++;
}
void input(){
maxx=-INF;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&bian[i].u,&bian[i].v,&bian[i].w);
maxx=max(maxx,bian[i].w);
}
}
void build(int mid){
mem(head,-1),tot=0;
for(int i=1;i<=m;i++){
add(bian[i].u,bian[i].v,bian[i].w-mid);
}
}
bool negativecycle(){//spfa判断“有向图负圈”问题,并非求最短路
queue<int> que;
mem(inq,0),mem(cnt,0);
for(int i=1;i<=n;i++) { inq[i]=1;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=edge[i].next){
int v=edge[i].to,w=edge[i].w;
if(d[v]>d[u]+w){
d[v]=d[u]+w;
if(!inq[v]) { que.push(v);inq[v]=1;if(++cnt[v]>n) return true; }
}
}
}
return false;
}
int main(){
//freopen("a.txt","r",stdin);
while(scanf("%d%d",&n,&m)!=EOF){
input();
build(1);
if(negativecycle()) { printf("No Solution\n"); continue; }
build(maxx+1);
if(!negativecycle()) { printf("Infinite\n"); continue; }
int l=1,r=maxx+1,mid;
while(l<r-1){//二分边界问题...仔细考虑
int mid=l+(r-l)/2;
build(mid);
if(negativecycle()) r=mid;//r是不符合条件的情况!
else l=mid;
}
printf("%d\n",l);
}
}