POJ 3204 求使得最大流增大的边

这题还是有点技巧的。

我们求出了最大流,怎样知道哪些边可以使得流量增大呢?


若存在e(U,V):S->U路径上的点都有剩余容量,V->T路径上的点都有剩余容量。

如果有某条边为上述情况,那么对边e进行扩容,最大流增大。

应该十分好理解吧。通过源点进行DFS寻找S集合中所有的点进行标号,很容易。

那么...V到T可达这怎么弄呢?

同样我们通过T来DFS只要存在点V到T有剩余容量,则V为集合T的点,在DFS(V);

由够边的特殊性,我们可以发现,当前边k的反向边就是k^1,通过k^1来判断有剩余容量就行了。

这样标号为2.

最后扫一遍边,连接两个集合的边为可扩容边。

PS:不要把反向边包含进去了....

#include<iostream>
#include<cstdio>
#define MN 1111
#define MM 22222
#define CC(a) memset(a,0,sizeof(a))
#define FF(i,m) for( int i=0;i<m;i++ )
#define INF 0x0FFFFFFF
template<class T>inline void checkmin( T &a,T b ){ if( a>b||a==-1 ) a=b; }
using namespace std;

struct edge{
   int u,pos,c,f,next;
}E[MM];
int N,M,s,t,alloc;
int head[MN],gap[MN],dis[MN],cur[MN],pre[MN];
void addEdge( int u,int v,int c,int cc=0 )
{
 	 E[alloc].u=u;
 	 E[alloc].pos=v;
 	 E[alloc].c=c;
 	 E[alloc].next=head[u];
 	 head[u]=alloc++;
 	 E[alloc].u=v;
 	 E[alloc].pos=u;
 	 E[alloc].c=cc;
 	 E[alloc].next=head[v];
 	 head[v]=alloc++;
}

void setG()
{
 	 CC(E);alloc=0;
 	 s=0;t=N-1;
 	 memset( head,-1,sizeof(head) );
	 int u,v,c;
	 while( M-- ){
  	  		scanf("%d%d%d",&u,&v,&c);
  	  		addEdge( u,v,c );
  	 }
}

int sap()
{
 	CC(dis),CC(gap);
 	FF(i,t+1) cur[i]=head[i];
 	int u=pre[s]=s,maxflow=0,aug=-1;
 	gap[0]=t+1;
 	while( dis[s]<=t ){
loop:
	 	   for( int &i=cur[u];i!=-1;i=E[i].next )
	 	   {
		   		int v=E[i].pos;
		   		if( E[i].c-E[i].f && dis[u]==dis[v]+1 )
		   		{
				 	pre[v]=u;
				 	checkmin(aug,E[i].c-E[i].f);
				 	u=v;
				 	if( v==t )
				 	{
					 	maxflow+=aug;
					 	for( u=pre[u];v!=s;v=u,u=pre[u] )
					 		 E[cur[u]].f+=aug,E[cur[u]^1].f-=aug;
					 	aug=-1;
		 		 	}
		 		 	goto loop;
				}
		   }
		   int mind=t;
		   for( int i=head[u];i!=-1;i=E[i].next )
		   {
		   		int v=E[i].pos;
		   		if( E[i].c-E[i].f && mind>dis[v] )
		   			cur[u]=i,mind=dis[v];
   		   }
   		   if( --gap[dis[u]]==0 )break;
   		   gap[dis[u]=mind+1]++;
   		   u=pre[u];
  	}
  	return maxflow;
}

int flag[MN];
void dfss( int cur )
{
 	 flag[cur]=1;
 	 for( int v=head[cur];v!=-1;v=E[v].next )
 	 if( !flag[E[v].pos]&&(E[v].c-E[v].f) )
 	 	 dfss(E[v].pos);
}
void dfst( int cur )
{
 	 flag[cur]=2;
 	 for( int v=head[cur];v!=-1;v=E[v].next )
 	 if( !flag[E[v].pos]&&(E[v^1].c-E[v^1].f) )
 	 	 dfst(E[v].pos);
}

int main()
{
 	while( scanf("%d%d",&N,&M)!=EOF )
 	{
	 	   setG();
	 	   int ans=0;
	 	   int maxflow=sap();
	 	   //printf( "%d\n",maxflow );
	 	   CC(flag);
	 	   dfss(s);dfst(t);
	 	   for( int i=0;i<alloc;i+=2 )
	 	   if( flag[E[i].u]==1&&flag[E[i].pos]==2&&E[i].c==E[i].f )
	 	   	   ans++;
		   printf( "%d\n",ans );
  	}
 	return 0;
}
/*
4 6
0 1 4
0 2 2
1 2 2
1 3 1
2 3 7
2 0 1
ans:3
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值