网络流学习笔记

还在施工之中,先将就着看着吧

网络流中关于反向边的个人理解

建反向边的目的是,在你下一次找增广路的时候,沿着反向边走的流量就代表之前走这条路的那条增广路要回退多少,而这一次走的流量就顺着上一次的增广路把回退流量补上,而那些回退的流量就再去找别的路径

就像这样,路径2挤掉了路径1的2的流量,继续沿着路径1的线路走

而路径1被挤掉的流量就要回到上一个节点重新找路径

有点像反悔贪心

点击查看 网络流 EK算法 模板
#include<bits/stdc++.h>
using namespace std;
int n,m,s,t;//s为源点,t为汇点
struct edge
{
	int f,t;
	long long  c;//流量上限
};
edge edges[100000];
int cnt=1;//存边数,初始为1是为了后续取反边 
struct node
{
	long long  mf;//max_f 从 S 出发到这个点最大流量上限
	vector<int> to;//存出边
	int pre;//前驱边
};
node nodes[100000];
bool bfs()
{
	for(int yy=1; yy<=n; yy++)
	{
		nodes[yy].mf=0;//清空流量数组 
	}
	queue<int> q;//队列 
	q.push(s);//源点入队 
	nodes[s].mf=1e9;//标记源点的水流为无限大 
	while(!q.empty()) 
	{
		int now_=q.front();//现在在那个节点 
		q.pop();
		int to,edg;
		for(int k=0; k<nodes[now_].to.size(); k++)//遍历所有出边 
		{
			edg=nodes[now_].to[k];//现在在哪条边 
			to=edges[edg].t;//去到哪个节点 
			if(nodes[to].mf==0&&edges[edg].c)//如果要去的节点没被去过并且这条边的流量不为 0 
			{
				nodes[to].mf=min(nodes[now_].mf,edges[edg].c);//记录要去的节点的流量 
				nodes[to].pre=edg;//记录是从哪条边流过来的流量 
				q.push(to);//将要去的节点入队 
				if(to==t)//如果抵达汇点 
				{
					return 1;
				}
			}
		}
	}
	return 0;//到不了汇点 
}
long long  e_k()//EK 算法 
{
	long long  tot_f=0;//总流量 
	while(bfs())//如果还有增广路 
	{
		int now_=t;//现在在那个节点 
		while(now_!=s)//如果还没有回到原点 
		{
			int edg=nodes[now_].pre;//这个节点的流量是从哪条边来的 
			edges[edg].c-=nodes[t].mf;//将正边剩余流量减去这一条增广路的流量 
			edges[edg^1].c+=nodes[t].mf;//将反向边加上这一条增广路的流量 
			now_=edges[edg^1].t;//继续向前 
		}
		tot_f+=nodes[t].mf;//总流量加上这一条增广路的流量 
	}
	return tot_f;
}
int main()
{
	ios::sync_with_stdio(0);
	cin>>n>>m>>s>>t;
	long long a,b,c;
	for(int ww=1; ww<=m; ww++)
	{
		cin>>a>>b>>c;
		edges[++cnt].c=c;
		edges[cnt].f=a;
		edges[cnt].t=b;
		nodes[a].to.push_back(cnt);
		edges[++cnt].c=0;//建反向边,初始流量为零 
		edges[cnt].f=b;
		edges[cnt].t=a;
		nodes[b].to.push_back(cnt);
	}
	cout<<e_k();//跑EK算法 
	return 0;
}

点击查看 网络流 Dinic算法 板子
#include<bits/stdc++.h>
using namespace std;
struct edge
{
	long long  c;//每条边的剩余流量 
	int f,t;
};
edge edges[100000];
int cnt=1;//边数从1开始 
struct node
{
	long long mf;//最小的流量 
	int num;//
	int cur;//当前弧优化 
	vector<int> to;
	int d;//节点深度,层数
};
node nodes[100000];
int n,m,s,t;

inline bool bfs()//分层用的bfs 
{
	for(int ww=1; ww<=n; ww++)//将节点层数清空 
	{
		nodes[ww].d=0;
	}
	queue<int> q;//队列 
	q.push(s);
	nodes[s].d=1;
	int to,now_,edg;
	while(!q.empty())//队列不为空 
	{
		now_=q.front();
		q.pop();
		for(int i=0; i<nodes[now_].to.size(); i++)
		{
			edg=nodes[now_].to[i];
			to=edges[edg].t;
			if(nodes[to].d==0&&edges[edg].c)
			{
				nodes[to].d=nodes[now_].d+1;
				q.push(to);
				if(to==t)
				{
					return 1;

				}
			}
		}
	}
	return 0;
}
inline long long dfs(int now_,long long mf)
{
	if(now_==t)//如果到了汇点 
	{
		return mf;
	}
	long long sum=0;
	int edg,to;
	for(int i=nodes[now_].cur; i<nodes[now_].to.size(); i++)
	{
		edg=nodes[now_].to[i];//当前弧优化 
		nodes[now_].cur=i;
		to=edges[edg].t;
		if(nodes[to].d==nodes[now_].d+1&&edges[edg].c)//如果在下一层 
		{
			long long f=dfs(to,min(mf,edges[edg].c));//算出流量 
			edges[edg].c-=f;
			edges[edg^1].c+=f;
			sum+=f;//
			mf-=f;//将剩余流量减去这个流量 
			if(mf==0)//优化残量优化 
			{
				break;
			}
		}
	}
	if(sum==0)//如果没有任何更多的流量 
	{
		nodes[now_].d=0;//将这个节点删去 
	}
	return sum;
}
long long dinic()
{
	long long  tot_f=0;
	while(bfs())//如果还有增广路的话 
	{
		for(int yy=1;yy<=n;yy++)
		{
			nodes[yy].cur=0;//将当前弧清空 
		}
		tot_f+=dfs(s,1e9);//加上新流量 
	}
	return tot_f;
}
int main()
{
	ios::sync_with_stdio(0);
	cin>>n>>m>>s>>t;
	long long  a,b,c;
	for(int ee=1; ee<=m; ee++)
	{
		cin>>a>>b>>c;
		edges[++cnt].f=a;
		edges[cnt].t=b;
		edges[cnt].c=c;
		nodes[a].to.push_back(cnt);
		edges[++cnt].f=b;
		edges[cnt].t=a;
		edges[cnt].c=0;
		nodes[b].to.push_back(cnt);
	}
	cout<<dinic();
	return 0;
}

posted @ 2024-10-11 20:42  sea-and-sky  阅读(9)  评论(0编辑  收藏  举报