首页 > 其他分享 >最大流与最小割

最大流与最小割

时间:2023-07-11 12:14:14浏览次数:25  
标签:head qu 最大 int ed ll 最小 ptop

最大流问题

给出起点、终点、边最大能传递的值,问从起点到终点最多能传多少

  • 阻塞流:不能再给终点增加值的流(最大流就是一种阻塞流)
  • 传统算法:新建一个剩余量的图,找路径、减去最小值、删路径,重复直到为阻塞流(不一定为最优解)

Ford-Fulkerson算法(复杂度O(fm),没什么用,过不了模板题)

相对于传统算法,它加入了一步建立反向边的步骤使得它可以"反悔"

1.找路径

2.减去最小值并建立反向的最小值边

3.重复

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll ans = 0;
const int MAXN = 201,MAXM = 5e3 + 10;
struct edge{
	ll v,val,i;
	edge* nex;
}ed[MAXM*2];
int ptop;
edge* head[MAXN];
void add(int u,int v,ll val){
	ed[ptop].i = ptop;
	ed[ptop].v = v;
	ed[ptop].nex = head[u];
	ed[ptop].val = val;
	head[u] = &ed[ptop];
	ptop++;
	ed[ptop].v = u;
	ed[ptop].i = ptop;
	ed[ptop].nex = head[v];
	ed[ptop].val = 0;
	head[v] = &ed[ptop];
	ptop++;
}
int s,t;
int ask[MAXM*2],use[MAXN];
bool find(int u){
	if(u == t)
		return 1;
	edge* p = head[u];
	for(edge* p = head[u];p != NULL;p = p -> nex){
		int v = p -> v;
		if(use[v] || p -> val == 0)
			continue;
		else{
			use[v] = 1;
			ask[++ptop] = p -> i;
			if(find(v))	return 1;
			ptop--;
		}	
	}
	return 0;
}
bool bfs(){
	ptop = 0;
	memset(use,0,sizeof(use));
	use[s] = 1;
	if(!find(s))
		return 0;
	ll mi = (ll)1 << 50;
	for(int i = 1;i <= ptop;i++){
		mi = min(mi,ed[ask[i]].val);
	}
	for(int i = 1;i <= ptop;i++){
		ed[ask[i]].val -= mi;
		ed[ask[i]^1].val += mi;
	}
	ans += mi;
	return 1;
}
int main()
{
	int n,m;cin >> n >> m >> s >> t;
	for(int i = 1;i <= m;i++){
		ll u,v,val;cin >> u >> v >> val;
		add(u,v,val);
	}
	while(bfs());
	cout << ans;
}

Edmonds-Karp算法(用最短路找边,时间复杂度为O(\(m^2n\)))(SAP算法)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll ans = 0;
const int MAXN = 201,MAXM = 5e3 + 10;
struct edge{
	ll u,v,val,i;
	edge* nex;
}ed[MAXM*2];
int ptop = 2;
edge* head[MAXN];
void add(int u,int v,ll val){
	ed[ptop].i = ptop;
	ed[ptop].v = v;
	ed[ptop].u = u;
	ed[ptop].nex = head[u];
	ed[ptop].val = val;
	head[u] = &ed[ptop];
	ptop++;
	ed[ptop].v = u;
	ed[ptop].u = v;
	ed[ptop].i = ptop;
	ed[ptop].nex = head[v];
	ed[ptop].val = 0;
	head[v] = &ed[ptop];
	ptop++;
}
int s,t;
int pre[MAXN*2],use[MAXN];
bool find(){
	queue<int> qu;qu.push(s);
	while(!qu.empty()){
		int x = qu.front();
		qu.pop();
		for(edge* p = head[x];p != NULL;p = p -> nex){
			int v = p -> v;
			if(use[v] || p -> val == 0)
				continue;
			pre[v] = p -> i;
			use[v] = 1;
			if(v == t)
				return 1;
			qu.push(v);
		}
	}
	return 0;
}
bool bfs(){
	ptop = 0;
	memset(use,0,sizeof(use));
	use[s] = 1;
	if(!find())
		return 0;
	ll mi = (ll)1 << 50;
	for(int i = pre[t];i != 0;i = pre[ed[i].u]){
		mi = min(mi,ed[i].val);
	}
	for(int i = pre[t];i != 0;i = pre[ed[i].u]){
		ed[i].val -= mi;
		ed[i^1].val += mi;
	}
	ans += mi;
	return 1;
}
int main()
{
	int n,m;cin >> n >> m >> s >> t;
	for(int i = 1;i <= m;i++){
		ll u,v,val;cin >> u >> v >> val;
		add(u,v,val);
	}
	while(bfs());
	cout << ans;
}

Dinic算法

  • level-up图:就是分层图,起点开始,终点结束,中间分层
  • Edmonds-Karp算法每次只解决一条边,效率较低,Dinic算法通过level-up图算一个图的阻塞流,效率更高
  • BFS找level-up图思路:记录深度,最后看终点有没有被标记深度即可
  • DFS找阻塞流:每次只找深度加1的点,深入DFS并传递边的最小值,返回分配的流,让这条边减去返回值,继续找,直到找完或者没有流可以继续了
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
const int MAXN = 201,MAXM = 5e3 + 10;
struct edge{
	int v;
	ll w;
	edge* nex;
	int i;
}ed[MAXM*2];
edge* head[MAXN];
int ptop = 0;
void add(int u,int v,ll w){
	ed[ptop].w = w;
	ed[ptop].v = v;
	ed[ptop].nex = head[u];
	head[u] = &ed[ptop];
	ed[ptop].i = ptop;
	ptop++;
	ed[ptop].w = 0;
	ed[ptop].v = u;
	ed[ptop].nex = head[v];
	head[v] = &ed[ptop];//构造反向边
	ed[ptop].i = ptop;
	ptop++;
}
int n,m,s,t;
int d[MAXN];//记录深度
bool bfs(){//构造levelup图
	queue<int> qu;
	memset(d,-1,sizeof(d));
	d[s] = 0;
	qu.push(s);//放入起点
	while(!qu.empty()){
		int u = qu.front();
		qu.pop();
		edge* p = head[u];
		while(p != NULL){
			int v = p -> v;
			if(p -> w && d[v] == -1){//存在且还没加入图
				d[v] = d[u] + 1;
				qu.push(v);
			}
			p = p -> nex;
		}
	}
	if(d[t] == -1)//到不了终点
		return 0;
	else
		return 1;
}
ll dfs(int u,ll flow){
	if(u == t)
		return flow;
	edge* p = head[u];
	ll use = 0;
	while(p != NULL){
		int v = p -> v;
		if(d[v] == d[u] + 1 && p -> w){
			ll tem = dfs(v,min(flow,p -> w));
			flow -= tem;
			ed[p -> i].w -= tem;
			ed[(p -> i) ^ 1].w += tem;
			use += tem;
			if(flow == 0)
				break;
		}
		p = p -> nex;
	}
	if(use == 0) d[u] = -1;
	return use;
}
ll dinic(){
	ll ans = 0;
	while(bfs()){
		ans += dfs(s,inf);
	}
	return ans;
}
int main()
{
	cin >> n >> m >> s >> t;
	for(int i = 1;i <= m;i++){
		int u,v,w;cin >> u >> v >> w;
		add(u,v,w);
	}
	cout << dinic();
}

ISAP算法

最小割等价于最大流问题

  • 因为所有流都要经过割,因此所有流都大于等于割,那么最大的流就等价于最小的割
  • 随便用一个最大流算法处理图,从起点出发将所有找到的点加入S集合,另外的为T集合,这样就找到最小割了(不唯一)

EK(SPFA版)费用流

  • 费用流:最大流中费用最小的

  • EK算法每次都找一条可行路径,那么只要一直找费用最小的可行路径即可

  • 每次都找一条费用最小的路径,反向边的费用是正向边的负权值

#include<bits/stdc++.h>
using namespace std;

const int N = 5e4 + 10,M = 5e5 + 10,INF = 0x7f7f7f7f;
int n,m,s,t,ans1,ans2;

struct edge{
	int v,flow,cost,i;
	edge* nex;
}ed[M];
int ptop = 0;
edge* head[N];
void add(int u,int v,int flow,int cost){
	ed[ptop].v = v;
	ed[ptop].flow = flow;
	ed[ptop].cost = cost;
	ed[ptop].i = ptop;
	ed[ptop].nex = head[u];
	head[u] = &ed[ptop];
	ptop++;
	ed[ptop].v = u;
	ed[ptop].flow = 0;
	ed[ptop].cost = -cost;//反向路径费用为负
	ed[ptop].i = ptop;
	ed[ptop].nex = head[v];
	head[v] = &ed[ptop];
	ptop++;
}

int pre[M],newcost[N],Flow[N];
bool vis[N];

inline bool spfa(){
	queue<int> qu;qu.push(s);vis[s] = 1;
	memset(Flow,0,sizeof(Flow));Flow[s] = INF;//起点流量无限
	memset(newcost,INF,sizeof(newcost));newcost[s] = 0;//起点费用为0
	memset(pre,0,sizeof(pre));
	while(!qu.empty()){
		int u = qu.front();
		qu.pop();
		vis[u] = 0;
		for(auto* p = head[u];p != NULL;p = p -> nex){
			int v = p -> v;
			if(p -> flow > 0 && newcost[v] > newcost[u] + p -> cost){//是否能更新这个点
				newcost[v] = newcost[u] + p -> cost;//更新点的费用
				Flow[v] = min(Flow[u],p -> flow);//更新流量
				pre[v] = p -> i;//记录这条边
				if(!vis[v]){vis[v] = 1,qu.push(v);}
			}
		}
	}
	return newcost[t] != INF;
}

void EK(){
	while(spfa()){
		ans1 += Flow[t],ans2 += newcost[t]*Flow[t];
		int u = t;
		while(u != s){
			int k = pre[u];
			ed[k].flow -= Flow[t];
			ed[k^1].flow += Flow[t];
			u = ed[k^1].v;
		}
	}
}

int main(){
	cin >> n >> m >> s >> t;
	for(int i = 1;i <= m;i++){
		int u,v,flow,cost;
		cin >> u >> v >> flow >> cost;
		add(u,v,flow,cost);
	}
	EK();
	cout << ans1 << ' ' << ans2 << endl;
}

标签:head,qu,最大,int,ed,ll,最小,ptop
From: https://www.cnblogs.com/xxcdsg/p/17544275.html

相关文章

  • 最小生成树
    最小生成树定义边权和最小的生成树Kruskal算法让边从小到大排序,如果不在同一集合,就加入#include<bits/stdc++.h>usingnamespacestd;constintMAXN=5e3+10,MAXM=2e5+10;intn,m;inta[MAXN];intfind(intx){ if(a[x]==x)returnx; elsereturna[......
  • layui layer.open弹框iframe最大化下面有空白处理
    思考为什么最大化会导致下面有空白这个时候我们去看这个时候就会发现其实他并没有任何去设置了他的一个高度我们的一个高度只不过是基于我们的弹框设置了多少高度那么他就是多少高度最终需要最大化处理一下他的那个方法 需要去获取到他的谈款的iframe这个引入然后去处理掉他。......
  • 代码随想录算法训练营第二十九天| 1005.K次取反后最大化的数组和 134. 加油站 135. 分
      860.柠檬水找零 思路:遇到20,先给10和5,再给三个5代码:1boollemonadeChange(vector<int>&bills){2if(bills.size()==0)returntrue;34map<int,int>currentMoney;5for(inti=0;i<bills.size();i++)6{7if......
  • 配电网多目标动态无功优化 基于IEEE33节点配电网,以配电网网损最小 电压偏差最小以及
    配电网多目标动态无功优化基于IEEE33节点配电网,以配电网网损最小电压偏差最小以及光伏消纳最大为目标,考虑了24个不同时刻的时间尺度,以光伏接入容量,变压器变比和两个无功补偿接入的容量为优化变量,通过多目标粒子群算法进行求解,得到最佳接入策略根据你提供的代码,我将对程序进行详......
  • 图的应用--最小生成树
    图的应用--最小生成树生成树概念:所有顶点均由边连接在一起.但不存在回路.一个图可以有许多不同的生成树.生成树特点:生成树的顶点个数与图的顶点个数相同.生成树是图的极小联通子图,去掉一条边则非联通一个有n个顶点的连通图的生成树有n-1条边在生成树中再加一条边必然......
  • 村子(最小化)
    解题:贪心很明显越靠近越好。随便从一个点出发,按照翻的排列方式来选择和父亲链接还是和兄弟链接。记得每次加2哦~~~~具体代码:#include<bits/stdc++.h>usingnamespacestd;#defineintlonglongconstintN=1e5+500;intn,par[N],swp[N],p[N];vector<int>g[N],re......
  • LeetCode 215. 数组中的第K个最大元素
    小根堆classSolution{public:intfindKthLargest(vector<int>&nums,intk){priority_queue<int,vector<int>,greater<int>>q;for(autox:nums){if(q.size()<k)q.push(x);......
  • 粒子群算法PSO优化LSSVM最小二乘支持向量机惩罚参数c和核函数参数g,用于回归预测,有例子
    粒子群算法PSO优化LSSVM最小二乘支持向量机惩罚参数c和核函数参数g,用于回归预测,有例子,易上手,简单粗暴,直接替换数据即可。仅适应于windows系统。质量保证,完美运行。这段程序主要是一个基于粒子群优化算法(ParticleSwarmOptimization,PSO)的支持向量机(SupportVectorMachine,SVM)......
  • 永磁同步电机参数辩识,采用最小二乘法进行的仿真
    永磁同步电机参数辩识,采用最小二乘法进行的仿真ID:9850625716661035......
  • 永磁同步电机参数辩识仿真 采用最小二乘法进行的仿真
    永磁同步电机参数辩识仿真采用最小二乘法进行的仿真ID:7550627011247044......