最大流问题(EK算法)

洛谷3376
给出一个网络图,以及其源点和汇点,求出其网络最大流。

EK算法:
在每条边都加了一条反向的,权值为0的边
反向边的作用:留下一个标记,让后面的流有机会让前面的流走另一条路。
直接从s到t广搜即可,从s开始不断向外广搜,通过权值大于0的边,直到找到t为止,(在过程中记录下每条边的编号),然后找到该路径上边权最小的边,记为mi,然后最大流加mi,然后把该路径上的每一条边的边权减去mi,每条的反向边都加上mi,直到找不到一条增广路为止。
怎么通过一条边的编号求它的反向边的编号???
由于正向边和反向边是一起加的,所以反向边的编号与正向边的编号只相差1。(异或1即可)

#include <cstdio>
#include <queue>
#include <cstring> 
using namespace std;
#define INF 0x7fffffff

struct Edge {
	int to,next,val;
} edge[200010];
int head[10005],cnt = 0;

void add(int u,int v,int val)
{
	edge[cnt].to = v;
	edge[cnt].val = val;
	edge[cnt].next = head[u];
	head[u] = cnt++;
}

int s,e;
int vis[10005],preid[10005],tag[10005];
//vis[i]表示在本次bfs中i是否被访问过
//preid[i]表示i的前一个节点编号
//tag[i]表示preid到i的边的下标编号 
 
bool bfs()
{
	memset(preid,0,sizeof(preid));
	memset(tag,0,sizeof(tag));
	memset(vis,0,sizeof(vis));
	queue<int> q;
	q.push(s);         //把起点入队 
	vis[s] = 1; 
	while( !q.empty() )
	{
		int x = q.front();
		q.pop();
		for (int i = head[x]; i != -1; i = edge[i].next)  //遍历与x相邻的节点 
		{
			Edge t = edge[i];
			if( vis[t.to] ) continue;
			if( t.val )     //如果这条边的权值大于0,表明可以走 
			{
				vis[t.to] = 1;
				preid[t.to] = x;    //preid置为x 
				tag[t.to] = i;      //tag置为i 
				if( t.to == e ) return true;   //到终点说明成功找到一条路径 
				q.push(t.to); 
			}
		}
	}
	return false;
}

int EK()
{
	int ans = 0;
	while( bfs() )
	{
		int mi = INF;
		for (int t = e; t != s; t = preid[t])
		{
			mi = min(mi,edge[tag[t]].val);    //回溯找到最小流量 
		}
		for (int t = e; t != s; t = preid[t])
		{
			edge[tag[t]].val -= mi;         //正边减去最小流量 
			edge[tag[t]^1].val += mi;       //负边加上最小流量 
		}	
		ans += mi;    //答案加上流量 
	}
	return ans;
}

int main()
{
	int n,m;
	scanf("%d%d%d%d",&n,&m,&s,&e);
	for (int i = 1; i <= n; i++)
	{
		head[i] = -1;
	}
	for (int i = 1; i <= m; i++)
	{
		int x,y,v;
		scanf("%d%d%d",&x,&y,&v);
		add(x,y,v);
		add(y,x,0);
	}
	printf("%d\n",EK());
	return 0;
}

算法复杂度上界为O(VE)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值