世界真的很大
图论的算法是个神奇东西
上次做了这道题就深有感触了
差分约束也是个差不多的道理,spfa
先看下题吧:
description:
给定一个有向图,边有权值,每次操作指定一个点u,一个值d,使所有u的出边+d,所有入边-d。问经过数次操作后,最小正边权最大为多少。如可以无限大就输出Infinite,如不能为正就输出No Solution
input
多组数据,输入到EOF
每组数据第一行包含2个整数n(点),m(边)
接下来m行每行包含3个整数u,v,w
表示u到v有一条边权为w的边
output
每组数据一个输出表示答案
要求输出最小的最大,自然想到二分答案
每次二分一个权值val,check能否找到一个方案使所有边的权值大于val
考虑任意一个点,他的每条入边和每条出边的增加(减少)的量肯定是相同的,设为sumi
考虑一条边(a,b),其操作之后的权值为w(a,b)+suma-sumb ,权值是大于val的,所以:
w(a,b)+suma-sumb > val
移相之后得到:
sumb-suma <= w(a,b)-val
sumb <= w(a,b)-val + suma
这就是差分约束的典型等式了
考虑SPFA中处理的等式
dis[v]< w(u,v) + dis[u]
不是有点相似吗,这里w(a,b)-val和w(u,v)都是一个固定的常量,像这样的这一串式子就可以用像SPFA的方法处理它,如果SPFA能跑出来就说明跑出来了一组可行解,suma就是a点的dis值
如果跑不出来,说明有负环,说明找不到一组可行解
那对于sumb <= w(a,b)-val + suma
我们就连一条a到b,权值为w(a,b)-val的边,SPFA需要一个源点,因为是单源最短路嘛,所以我们就加一个无关紧要的源点S,使其与所有点连边,边权为0
题目规定了上界,所以只要判一下val为最上界是否满足有解,如果是,就输出Infinite
题目规定为正数,所以判一下下界1,如果都不行,无解的话就输出No Solution
完整代码:
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int INF=0x3f3f3f3f;
struct edge
{
int v,last,w;
}ed[100010];
queue <int> state;
int head[100010],dis[100010],se[100010],book[100010];
int n,m,ans,num=0,S=0,big=0;
void init()
{
num=0,ans=0,S=0,big=0;
memset(head,0,sizeof(head));
}
void add(int u,int v,int w)
{
num++;
ed[num].v=v;
ed[num].w=w;
ed[num].last=head[u];
head[u]=num;
}
bool SPFA()
{
memset(book,0,sizeof(book));
memset(dis,0x3f3f3f3f,sizeof(dis));
memset(se,0,sizeof(se));
while(!state.empty()) state.pop();
state.push(S);
se[S]=1,dis[S]=0,book[S]=1;
while(!state.empty())
{
int u=state.front();
se[u]=0;state.pop();
for(int i=head[u];i;i=ed[i].last)
{
int v=ed[i].v;
if(dis[v]>dis[u]+ed[i].w)
{
dis[v]=dis[u]+ed[i].w;
if(!se[v])
{
se[v]=1;
state.push(v);
book[v]++;
if(book[v]>n+1) return 0;
}
}
}
}
return 1;
}
bool check(int val)
{
bool flag;
for(int i=1;i<=num;i++)
ed[i].w-=val;
flag=SPFA();
for(int i=1;i<=num;i++)
ed[i].w+=val;
return flag;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
init();
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
big=max(big,w);
}
for(int i=1;i<=n;i++)
add(S,i,0);
if(check(big+1))
{
printf("Infinite\n");
continue ;
}
else if(!check(1))
{
printf("No Solution\n");
continue ;
}
int lf=1,rg=big;
while(lf<=rg)
{
int mid=(lf+rg)>>1;
if(check(mid))
{
ans=mid;
lf=mid+1;
}
else rg=mid-1;
}
printf("%d\n",ans);
}
}
/*
Whoso pulleth out this sword from this stone and anvil is duly born King of all England
*/
嗯,就是这样