RE了好几次,后来发现是边开小了,这个模板又是达哥的,我继承了达哥的模板数据。。。
SAP算法 最短增广路算法(Shortest Augmenting Path Algorithm),是网络流中求最大流的经典算法之一,即每次寻找包含弧的个数最少的增广路进行增广,可以证明,此算法最多只需要进行mn/2次增广。并且引入距离标号的概念,可以在O(n)的时间里找到一条最短增广路。最终的时间复杂度为O(n^2m),但在实践中,时间复杂度远远小于理论值(特别是加了优化之后),因此还是很实用的。
距离标号:
对于每个顶点i赋予一个非负整数值d(i)来描述i到t的“距离”远近,称它为距离标号,并且满足以下两个条件: 1. d(t)=0 2. 对于残留网络Gf中的一条弧(i,j),d(i)≤d(j)+1。
允许弧和允许路:
如果残留网络Gf中的一条弧(i,j)满足d(i)=d(j)+1,我们称(i,j)是允许弧,由允许弧组成的一条s-t路径是允许路。显然,允许路是残留网络Gf中的一条最短增广路。当找不到允许路的时候,我们需要修改某些点的d(i)。
Gap优化:
我们可以注意到由于残留网络的修改只会使d(i)越来越大(因为修改前d(i)<d(j)+1,而修改后会存在d(i)=d(j)+1,因此变大了),所以说d(i)是单调递增的,这就提示我们,如果d函数出现了“断层”,即没有d(i)=k,而有d(i)=k±1,这时候必定无法再找到增广路径。我们可以这么想,现在的i满足d(i)=k+1,发现没有一个d(j)为k,因此就会尝试去调整d(i),但是d(i)是单调递增的,只会越来越大,所以k这个空缺便永远不会被补上,也就是说无法再找到增广路径。
当前弧优化:
可以注意到一个事实:如果说在某次迭代中从i出发的弧(i,j)不是允许弧,则在顶点i的标号修改之前(i,j)都不可能是允许弧。(因为d(i)不变,d(j)不减且d(i)<d(j)+1)这样,在查找允许弧的时候只需要从上一次找到的允许弧开始找。所以我们增加“当前弧”这个数据结构,记录当前顶点找到的允许弧,只有在修改这个顶点标号时才会更改这个顶点的当前弧。
#include <cstdio>
#include <string.h>
const int N=20005;
const int M=890000;//边是双向存的(注意不是无向)要开正常的2倍大
const int inf=0x7fffffff;
int head[N];
struct Edge{
int v,next,w;
} edge[M];
int cnt,n,s=0,t;
void addedge(int u,int v,int w)//这里存的还是一条有向边
{
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt++;
edge[cnt].v=u;
edge[cnt].w=0;
edge[cnt].next=head[v];
head[v]=cnt++;
}
int sap(){
int pre[N],cur[N],dis[N],gap[N];
int flow=0,aug=inf,u;
bool flag;
for(int i=0; i<n; i++){
cur[i]=head[i];
gap[i]=dis[i]=0;
}
gap[s]=n;
u=pre[s]=s;
while(dis[s]<n){
flag=0;
for(int &j=cur[u]; j!=-1; j=edge[j].next){
int v=edge[j].v;
if(edge[j].w>0&&dis[u]==dis[v]+1){
flag=1;
if(edge[j].w<aug) aug=edge[j].w;
pre[v]=u;
u=v;
if(u==t){
flow+=aug;
//printf("%d\n",flow);
while(u!=s){
u=pre[u];
edge[cur[u]].w-=aug;
edge[cur[u]^1].w+=aug;//异或是找与其配对的边
}
aug=inf;
}
break;
}
}
if(flag) continue;
int mindis=n;
for(int j=head[u]; j!=-1; j=edge[j].next){
int v=edge[j].v;
if(edge[j].w>0&&dis[v]<mindis){
mindis=dis[v];
cur[u]=j;
}
}
if((--gap[dis[u]])==0)
break;
gap[dis[u]=mindis+1]++;
u=pre[u];
}
return flow;
}
int main(){
int m,a,b,u,v,w;
while(~scanf("%d%d",&n,&m))
{
n++;
t=n;
cnt=0;
memset (head , -1 , sizeof(head));
for (int i=1 ; i<n ; ++i)
{
scanf("%d%d",&a,&b);
addedge(0,i,a);
addedge(i,t,b);
}
for (int i=0 ; i<m ; ++i)
{
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
n++;
printf("%d\n",sap());
}
return 0;
}