网络流

1.Edmonds-Karp 最短增广路算法

算法的时间复杂度上限为O(n*m^2),(n是顶点数,m是边数)。

POJ1273 Drainage Ditches

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxm=210;
const int INF=0x3f3f3f3f;
int mp[maxm][maxm];
int n,m;
int pre[maxm];  //路径上每个节点的前驱节点
bool vis[maxm];

unsigned Augment(){
    deque<int> q;
    memset(pre,0,sizeof(pre));
    memset(vis,0,sizeof(vis));
    pre[1]=0;
    vis[1]=1;
    q.push_back(1);
    bool bFindPath=false;
    //用bfs寻找一条从源到汇的可行路径
    int v;
    while(!q.empty()){
        v=q.front();
        q.pop_front();
        for(int i=1;i<=m;i++){
            if(mp[v][i]>0&&!vis[i]){
                pre[i]=v;
                vis[i]=1;
                if(i==m){
                    bFindPath=true;
                    q.clear();
                    break;
                }else q.push_back(i);
            }
        }
    }

    if(!bFindPath) return 0;
    int nMinFlow=INF;
    v=m;
    //寻找源到汇路径上流量最小的边,其容量就是此次增加的总流量
    while(pre[v]){
        nMinFlow=min(nMinFlow,mp[pre[v]][v]);
        v=pre[v];
    }
    //沿此路径添加反向边,同时修改路径上每条边的容量
    v=m;
    while(pre[v]){
        mp[pre[v]][v]-=nMinFlow;
        mp[v][pre[v]]+=nMinFlow;
        v=pre[v];
    }
    return nMinFlow;
}

int main(){
    while(~scanf("%d%d",&n,&m)){ //m是顶点数目,编号从1开始
        memset(mp,0,sizeof(mp));
        int s,e,c;
        for(int i=0;i<n;i++){
            scanf("%d%d%d",&s,&e,&c);
            mp[s][e]+=c;  //两点之间可能存在多条边
        }
        unsigned int MaxFlow=0;
        unsigned int aug;
        while(aug=Augment()){
           // printf("aug=%d\n",aug);
            MaxFlow+=aug;
        }

        printf("%d\n",MaxFlow);
    }
    return 0;
}

2.Dinic快速网络流算法

#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
int G[300][300];
bool visited[300];
int Layer[300];
int n,m;  //1是源点,m是汇点 

bool CountLayer(){
	int layer=0;
	deque<int> q;
	memset(Layer,0xff,sizeof(Layer)); //都初始化为-1
	Layer[1]=0;
	q.push_back(1);
	while(!q.empty()){
		int v=q.front();
		q.pop_front();
		for(int i=1;i<=m;i++){
			if(G[v][i]>0&&Layer[i]==-1){ //Layer[i]==-1说明i还没有访问过 
				Layer[i]=Layer[v]+1;
				if(i==m) //分层到汇点即可 
					return true;
				else 
					q.push_back(i);
			}
		}
	}
	return false;
}

int Dinic(){
	int s;
	int nMaxFlow=0;
	deque<int> q;  //DFS用的栈
	while(CountLayer()){ //只要能分层 
		q.push_back(1); //汇点入栈 
		memset(visited,0,sizeof(visited));
		while(!q.empty()){
			int nd=q.back();
			if(nd==m){ //nd是汇点 
				int nMinC=INF;  //在栈中找到容量最小边 
				int nMinC_vs;  //容量最小边的起点 
				for(int i=1;i<q.size();i++){
					int vs=q[i-1];
					int ve=q[i];
					if(G[vs][ve]>0){
						if(nMinC>G[vs][ve]){
							nMinC=G[vs][ve];
							nMinC_vs=vs;
						}
					}
				}
				
				//增广,改图
				nMaxFlow+=nMinC;
				for(int i=1;i<q.size();i++){
					int vs=q[i-1];
					int ve=q[i];
					G[vs][ve]-=nMinC; //修改边容量 
					G[ve][vs]+=nMinC; //添加反向边 
				} 
				//退栈到nMinC_vs成为栈顶,以便继续dfs
				while(!q.empty()&&q.back()!=nMinC_vs){
					visited[q.back()]=0;
					q.pop_back();
				} 
			}
			else{ //nd不是汇点
				int i;
				for(i=1;i<=m;i++){
					if(G[nd][i]>0&&Layer[i]==Layer[nd]+1&&!visited[i]){
						//只往下一层没有走过的结点走 
						visited[i]=1;
						q.push_back(i);
						//cout<<"nd="<<nd<<",i="<<i<<endl;
						break;
					}
				}
				
				if(i>m) //找不到下一个点 
					q.pop_back(); //回溯 
			}
		}
	}
	return nMaxFlow;
}

int main(){
	while(~scanf("%d%d",&n,&m)){
		int s,e,c;
		memset(G,0,sizeof(G));
		for(int i=0;i<n;i++){
			scanf("%d%d%d",&s,&e,&c);
			G[s][e]+=c; //两点之间可能有多条边 
		}
		printf("%d\n",Dinic());
	}
	return 0;
}

POJ3436 ACM Computer Factory

题意:题意本身就比较难理解,静下心,慢慢看~

有n台机器组装电脑,电脑由p个部件组成,每台

#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
int G[110][110],tag[110][110];  //tag[i][j]表示从i到j流出的流量,如果值为负数,说明是流出,方便后面计算 
bool visited[110];
int Layer[110];
int n,s,t; 
 
bool CountLayer(){
	int layer=0;
	deque<int> q;
	memset(Layer,0xff,sizeof(Layer)); //都初始化为-1
	Layer[0]=0;
	q.push_back(0);
	while(!q.empty()){
		int v=q.front();
		q.pop_front();
		for(int i=0;i<=t;i++){
			if(G[v][i]>0&&Layer[i]==-1){ //Layer[i]==-1说明i还没有访问过 
				Layer[i]=Layer[v]+1;
				if(i==t) //分层到汇点即可 
					return true;
				else 
					q.push_back(i);
			}
		}
	}
	return false;
}
 
int Dinic(){
	int nMaxFlow=0;
	deque<int> q;  //DFS用的栈
	while(CountLayer()){ //只要能分层 
		q.push_back(s); //源点入栈 
		memset(visited,0,sizeof(visited));
		while(!q.empty()){
			int nd=q.back();
			if(nd==t){ //nd是汇点 
				int nMinC=INF;  //在栈中找到容量最小边 
				int nMinC_vs;  //容量最小边的起点 
				for(int i=1;i<q.size();i++){
					int vs=q[i-1];
					int ve=q[i]; 
					
					if(G[vs][ve]>0){
						//if(vs>n&&vs-n!=ve&&ve!=s||ve>n&&ve-n!=vs&&vs!=s) printf("vs=%d,ve=%d\n",vs,ve);
						if(nMinC>G[vs][ve]){
							nMinC=G[vs][ve];
							nMinC_vs=vs;
						}
					}
				}
				
				//增广,改图
				nMaxFlow+=nMinC;
				for(int i=1;i<q.size();i++){
					int vs=q[i-1];
					int ve=q[i];
					G[vs][ve]-=nMinC; //修改边容量 
					G[ve][vs]+=nMinC; //添加反向边 
					tag[vs][ve]-=nMinC;
					tag[ve][vs]+=nMinC;
				} 
				//退栈到nMinC_vs成为栈顶,以便继续dfs
				while(!q.empty()&&q.back()!=nMinC_vs){
					visited[q.back()]=0;
					q.pop_back();
				} 
			}
			else{ //nd不是汇点
				int i;
				for(i=0;i<=t;i++){
					if(G[nd][i]>0&&Layer[i]==Layer[nd]+1&&!visited[i]){
						//只往下一层没有走过的结点走 
						visited[i]=1;
						q.push_back(i);
						//cout<<"nd="<<nd<<",i="<<i<<endl;
						break;
					}
				}
				
				if(i>t) //找不到下一个点 
					q.pop_back(); //回溯 
			}
		}
	}
	return nMaxFlow;
}
 
int main(){
	int p,w[55],in[55][15],out[55][15];
	while(~scanf("%d%d",&p,&n)){
		memset(G,0,sizeof(G));
		memset(tag,0,sizeof(tag));
		s=0;
		t=2*n+1;
		bool flag;
		for(int i=1;i<=n;i++){
			scanf("%d",&w[i]);
			
			flag=true;
			for(int j=1;j<=p;j++){
				scanf("%d",&in[i][j]);
				if(in[i][j]==1) flag=false;  //判断是否与源点s连边 
			}
			if(flag)
				G[s][i]=INF;   
				
			flag=true;
			for(int j=1;j<=p;j++){
				scanf("%d",&out[i][j]);
				if(out[i][j]==0) flag=false;  //判断是否与汇点t连边 
			}
			if(flag)
				G[i+n][t]=INF;
		}
		
		for(int i=1;i<=n;i++){
			G[i][i+n]=w[i];    //内部连边 
			for(int j=1;j<=n;j++){
				flag=true;
				if(i==j) continue;
				for(int k=1;k<=p;k++){
					if(in[j][k]!=2&&out[i][k]!=in[j][k]){
						flag=false;
						break;
					}
				}
				if(flag){
					G[i+n][j]=INF;  //机器之间相连 
				}
			}
		}
			
		printf("%d ",Dinic());
		int cnt=0;
		for(int i=1;i<t;i++){
			for(int j=1;j<t;j++){
				if(i==j+n||j==i+n) continue;  //不是内部边、与源点相连的边、与汇点相连的边 
				if(tag[i][j]<0)  //值为负,说明有流量从i点流到j点,即对最大流有贡献 
					cnt++;
			}
		}
	
		printf("%d\n",cnt);
		
		for(int i=1;i<t;i++){
			for(int j=1;j<t;j++){
				if(i==j+n||j==i+n) continue;   //不是内部边、与源点相连的边、与汇点相连的边 
				if(tag[i][j]<0){
					printf("%d %d %d\n",i==n?i:i%n,j==n?j:j%n,tag[i][j]*(-1));
				}
			}
		}
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值