最大流问题——Edmonds-Karp&&Dinic算法模板

Edmonds-Karp算法简介

Edmonds-Karp算法使用BFS不断寻找增广路,直到没有增广路存在,则为最大流

Edmonds-Karp算法模板

时间复杂度为O(nm^2)

struct edge{   //储存边
	int from,to,flow,cap;
	edge(int u,int v,int f,int c):from(u),to(v),flow(f),cap(c){}
}

vector<edge>edges;
vector<int>g[maxn];
int a[manx],p[maxn];

void addedge(int from ,int to,int c){   //加边函数,若为无向边,则调用addedge(u,v,c);addedge(v,u,c);各一次
	edges.push_back(edge(from,to,0,c));
	edges.push_back(edge(to,from,0,0));   //加一条容量为0的反向边
	m=edges.size();
	g[from].push_back(m-2);
	g[to].push_back(m-1);
}

int Edmonds-Karp(int s,int t){
	int flow;
	while(1){
		memset(a,0,sizeof(a));
		queue<int>q;
		q.push(s);
		a[s]=INF;
		while(!q.empty()){
			int x=q.front();q.pop();
			for(int i=0;i<g[x].size();i++){
				edge& e=edges[g[x][i]];
				if(!a[e.to]&&e.cap>e.flow){
					p[e.to]=g[x][i];
					a[e.to]=min(a[x],e.cap-e.flow);
					q.push(e.to);
				}
			}
			if(a[t]) break;
		}
		if(!a[t]) break;
		for(int u=t;u!=s;u=edges[p[u]].from){
			edges[p[u].flow+=a[t];
			edges[p[u]^1].flow-=a[t];
		}
		flow+=a[t];
	}
	return flow;
}

Dinic算法简介

Dinic算法通过BFS构造层次图,然后用DFS用阻塞流来增广

Dinic算法模板

时间复杂度O(n^2m)

const int INF=1<<30;

struct edge{   //储存边
	int from,to,flow,cap;
	edge(int u,int v,int f,int c):from(u),to(v),flow(f),cap(c){}
}

vector<edge>edges;
vector<int>g[maxn];
int vis[manx],d[maxn]; //d[maxn]储存结点的层级
int cur[maxn] //记录每个结点考虑到的弧

void addedge(int from ,int to,int c){   //加边函数,若为无向边,则调用addedge(u,v,c);addedge(v,u,c);各一次
	edges.push_back(edge(from,to,0,c));
	edges.push_back(edge(to,from,0,0));   //加一条容量为0的反向边
	m=edges.size();
	g[from].push_back(m-2);
	g[to].push_back(m-1);
}


int bfs(){
	memset(vis,0,sizeof(vis));
	queue<int>q;
	q.push(s);
	d[s]=0;
	vis[s]=1;
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int i=0;i<g[x].size();i++){
			edge& e=edge[g[x][i]];
			if(!vis[e.to]&&e.cap>e.flow){
				vis[e.to]=1;
				d[e.to]=d[x]+1;
				q.push(e.to);
			}
		}
	}
	return vis[t];
}

int dfs(int x,int a){
	if(x==t||a==0) return a; //余量为0或者到达汇点时为边界
	int flow=0,f;
	for(int &i=cur[x];i<g[x].size();i++){
		cur[x]=i;
		edge& e=edges[g[x][i]];
		if(d[x]+1==d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0){
			e.flow+=f;
			edges[g[x][i]^1].flow-=f;
			flow+=f;
			a-=f;
			if(a==0) break;
		}
	}
	return flow;
}

int Dinic(int s,int t){
	int flow=0;
	while(bfs()){
		memset(cur,0,sizeof(cur));
		flow+=dfs(s,INF);
	}
	return flow;
}

当点有容量时

我们采用的策略是拆点:
即把每个点拆成左点到右点,建一条从左点到右点的边,边权为容量(此时,从原来的点出发的的边要建从该点右点出,从到达的点的左点进)

习题

1.UVA - 10330
https://cn.vjudge.net/problem/UVA-10330

最小割问题

最小割问题与最大流关系非常密切,那么什么是最小割呢。首先我们得知道什么是割,割就是把所有顶点分成两个顶点集S和T,源点s在S集,汇点t在T集,割的容量定义为起点在S集,终点在T集的所有边的容量之和。最小割就是问题就是找到一个分组,使得割的容量之和最小。可以证明,一个图的最小割等于其最大流,其证明可以看这篇bloghttps://www.cnblogs.com/dyzll/p/5887266.html
我的理解:最小割就是使得s到达不了t的舍弃的边的最小代价

下面我们来做一道题https://cn.vjudge.net/problem/46296/origin

由于奇数与奇数之间不满足第一条,偶数与偶数之间不满足第二条,所以从源点s各自连边一条边到所有奇数中,容量为奇数的值,从所有偶数各自连一条边到t中,容量为偶数的值,再逐个判定每奇数和偶数之间是否可以同时取,如果不可以同时取,则两个数之间连一条容量为INF的边。只要求得使s到达不了t的舍弃的边的最小代价(最小割)(因为如果s到达了t,代表经过一条不可以同时取的奇数和偶数连接起来的边)。
跑一遍最大流,然后用所有数之和减去最大流就得到答案啦。

ac代码

#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<queue>
#include<string.h>
#include<stdlib.h>
using namespace std;
typedef long long ll;

const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=6e5;
int n,s,t;
int head[5000],d[5000],a[5000],vis[5000],cur[5000];
int tot=0,to[maxn],nxt[maxn];
ll flow[maxn],cap[maxn];

int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
int judge(int a,int b){
    int c=a*a+b*b;
    int x=sqrt(c);
    if(gcd(a,b)==1&&x*x==c) return 1;
    return 0;
}


void addedge(int u,int v,ll ca){
    to[tot]=v,flow[tot]=0,cap[tot]=ca,nxt[tot]=head[u],head[u]=tot++;
    to[tot]=u,flow[tot]=0,cap[tot]=0,nxt[tot]=head[v],head[v]=tot++;
}

int bfs(){
    memset(vis,0,sizeof(vis));
    queue<int>q;
    q.push(s);
    d[s]=0;
    vis[s]=1;
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=head[x];~i;i=nxt[i]){
            if(!vis[to[i]]&&cap[i]>flow[i]){
                vis[to[i]]=1;
                d[to[i]]=d[x]+1;
                q.push(to[i]);
            }
        }
    }
    return vis[t];
}

ll dfs(int x,ll a){
    if(x==t||a==0) return a;
    ll flo=0,f;
    for(int &i=cur[x];~i;i=nxt[i]){
        cur[x]=i;
        if(d[x]+1==d[to[i]]&&(f=dfs(to[i],min(a,cap[i]-flow[i])))>0){
            flow[i]+=f;
            flow[i^1]-=f;
            flo+=f;
            a-=f;
            if(a==0) break;
        }
    }
    return flo;
}

ll dinic(){
    int flo=0;
    while(bfs()){
        for(int i=0;i<=n+1;i++){
            cur[i]=head[i];
        }
        flo+=dfs(s,INF);
    }
    return flo;
}

int main(){
    long long sum=0;
    memset(head,-1,sizeof(head));
    scanf("%d",&n);
    s=0;t=n+1;
    for(int i=1;i<=n;i++) {scanf("%d",a+i);sum=sum+a[i];}
    for(int i=1;i<=n;i++){
        if(a[i]&1){
            addedge(0,i,a[i]);
            for(int j=1;j<=n;j++){
                if(a[j]&1) continue;
                else if(judge(a[i],a[j])) addedge(i,j,INF);
            }
        }
        else{
            addedge(i,n+1,a[i]);
            for(int j=1;j<=n;j++){
                if(a[i]&1==0) continue;
                    else if(judge(a[i],a[j])) addedge(j,i,INF);
            }
        }
    }
    printf("%lld",sum-dinic());
    return 0;
}

平面图跑最大流边数过大

求出其对偶图,转化为最短路问题
http://blog.sina.com.cn/s/blog_60707c0f01011fnn.html

习题BZOJ2007

ac代码

#include<stdio.h>
#include<algorithm>
#include<stdlib.h>
#include<string.h>
#include<queue>
typedef long long ll;
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=300000;
const int maxm=4005*1005;
int done[maxn],head[maxn],d[maxn];
int to[maxm],nxt[maxm],dis[maxm];
int n,s,t,tot=0;

void addedge(int u,int v,int c){
    to[tot]=v;dis[tot]=c;nxt[tot]=head[u];head[u]=tot++;
}

struct node{		//用来结合优先队列使用
	int d,u;
	bool operator < (const node &rhs) const {
		return d>rhs.d;
	}
};

void dij(){
	priority_queue<node> q;
	for(int i=0;i<=t;i++) d[i]=INF;
	d[s]=0;
	memset(done,0,sizeof(done));
	q.push((node){0,s});
	while(!q.empty()){
		node x=q.top();q.pop();
		int u=x.u;
		if(done[u]) continue;
		done[u]=1;
		for(int i=head[u];~i;i=nxt[i]){
			if(d[to[i]]>d[u]+dis[i]){  	//松弛操作
				d[to[i]]=d[u]+dis[i];
				q.push((node){d[to[i]],to[i]});
			}
		}
	}
}


void build(){
    int c;
    tot=0;
    memset(head,-1,sizeof(head));
    scanf("%d",&n);
    s=0,t=n*n+1;
    for(int i=0;i<=n;i++){
        for(int j=1;j<=n;j++){
            scanf("%d",&c);
            if(i==0) addedge(j,t,c);
            else if(i==n) addedge(s,(n-1)*n+j,c);
            else addedge(i*n+j,(i-1)*n+j,c);
        }
    }
    for(int i=0;i<n;i++){
        for(int j=0;j<=n;j++){
            scanf("%d",&c);
            if(j==0) addedge(s,i*n+1,c);
            else if(j==n) addedge((i+1)*n,t,c);
            else addedge(i*n+j,i*n+j+1,c);
        }
    }
    for(int i=0;i<=n;i++){
        for(int j=1;j<=n;j++){
            scanf("%d",&c);
            if(i==0) addedge(t,j,c);
            else if(i==n) addedge((n-1)*n+j,s,c);
            else addedge((i-1)*n+j,i*n+j,c);
        }
    }
    for(int i=0;i<n;i++){
        for(int j=0;j<=n;j++){
            scanf("%d",&c);
            if(j==0) addedge(i*n+1,s,c);
            else if(j==n) addedge(t,(i+1)*n,c);
            else addedge(i*n+j+1,i*n+j,c);
        }
    }
}

int main(){
    build();
    dij();
    int flow=d[t];
    printf("%d",flow);
}

如有错误,欢迎指正

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页