Dinic/ISAP求最大流

最爱丁珰發表於2024-09-02

演算法執行過程見藍書和OI-wiki,當前弧最佳化見OI-wiki的描述,程式碼見下

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=10010,M=100010,inf=1<<29;
int now[N],End[M<<1],Next[M<<1],Len[M<<1],Last[N];
int n,m,s,t,cnt=1,d[N];
ll maxflow=0;
queue<int> q;
void add(int x,int y,int z)
{
	End[++cnt]=y,Next[cnt]=Last[x],Len[cnt]=z,Last[x]=cnt;
	End[++cnt]=x,Next[cnt]=Last[y],Len[cnt]=0,Last[y]=cnt;
}
bool bfs()//構建分層圖
{
	while(!q.empty()) q.pop();
	memset(d,0,sizeof(d));//d表示層次
	q.push(s),d[s]=1;
	now[s]=Last[s];//now是當前弧最佳化
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		for(int i=Last[x];i;i=Next[i])
		{
			int y=End[i];
			if(!Len[i]||d[y]) continue;
			q.push(y),d[y]=d[x]+1;
			now[y]=Last[y];//構建分層圖的時候,初始化當前弧
			if(y==t) return 1;
		}
	}
	return 0;
}
int dinic(int x,int flow)//dinic(x,flow)表示有flow流量流進x,最多有多少流量可以從x流出到t
{
	if(x==t) return flow;
	int rest=flow,k,i;//rest表示還沒有分配的剩餘的流量
	for(i=now[x];i&&rest;i=Next[i])
	if(Len[i]&&d[End[i]]==d[x]+1)//如果是分層圖上面的邊
	{   
	    now[x]=i;
	    //當前弧最佳化
	    //對於上一條分層圖的邊
	    //如果我們分配的流量是rest(也就是說rest<len)
	    //那麼就說明我們分配小於rest的流量就可以讓上一條邊堵塞(因為rest為0的話就會結束迴圈)
	    //所以接下來就都不用考慮上一條邊了,因為一定已經堵塞了
	    //如果我們分配的流量是len(也就是說rest>len)
	    //那麼無論len是否被用完,都可以說明上一條邊堵塞了,也不用考慮了
	    //如果將當前弧最佳化放在`return flow-rest`的上面一句
	    //那麼當前這個分層圖一定只會dfs到x這個點一次
	    //就會導致外面構建分層圖的bfs執行更多次
	    //於是TLE
	    //所以當前弧最佳化要放在這個位置
		k=dinic(End[i],min(rest,Len[i]));
		if(!k) d[End[i]]=0;//如果一點流量都流不出去,直接刪除這條邊
		Len[i]-=k;
		Len[i^1]+=k;//更新殘存網路
		rest-=k;
	}
	return flow-rest;
}
int main()
{
	scanf("%d%d%d%d",&n,&m,&s,&t);
	for(int i=1;i<=m;i++)
	{
		int u,v,c;
		scanf("%d%d%d",&u,&v,&c);
		add(u,v,c);
	}
	int flow;
	while(bfs()) 
	while(flow=dinic(s,inf))//其實這個while可以改成if,因為初始流量為無窮,走一遍就可以把每條以s為起點的邊流堵塞;也就是說這個while只會執行一次
	maxflow+=flow;
	printf("%lld",maxflow);
    return 0;
}

相關文章