网络流

网络流

一.最大流问题
1.增广路算法
1).Ford-Fulkerson算法: 残留网络中寻找增加路径
 STEP0:置初始可行流。
 STEP1:构造原网络的残量网络,在残量网络中找s-t有向路。如果没有,算法得到最大流结束。否则继续下一步。
 STEP2:依据残量网络中的s-t有向路写出对应到原网络中的s-t增广路。对于增广路中的前向弧,置s(e)=u(e)- f(e)。对于反向弧,置s(e)=f(e)                        

 STEP3:计算crement=min{s(e1),s(e2),…,s(ek)} 
 STEP4:对于增广路中的前向弧,令f(e)=f(e)+crement;对于其中的反向弧,令f(e)=f(e)-crement,转STEP1。

2).Edmonds-Korp算法

算法实现:

<span style="font-size:18px;"><strong>#include<stdio.h>
#include<string.h>
#include<queue>
#define M 0x7fffffff
#define Max 506
using namespace std;

int cap[Max][Max],f[Max][Max];
int pre[Max];         //记录增广路径 
int p[Max];           //记录增广时的残量
int n,m;

int EK(int s,int t)
{
	int v,u;
	int sum=0;
	queue<int >q;
	memset(f,0,sizeof(f));
	//memset(pre,0,sizeof(pre));
	while(true)            //BFS找增广路
	{
		memset(p,0,sizeof(p));
		p[s]=M;
		q.push(s);
		while(!q.empty())
		{
			u=q.front();
			q.pop();
			for(v=1;v<=m;v++)
			{
				if(!p[v] && f[u][v]<cap[u][v])      //找到新节点
				{
					pre[v]=u;           //记录v的父亲,并加入FIFO队列
					q.push(v);
					p[v]=p[u]<cap[u][v]-f[u][v]?p[u]:cap[u][v]-f[u][v];   //s-v路径上的最小残量 
				}
			}
		}
		if(p[t]==0) break;        //找不到增广路,则当前流已经是最大流(最小割最大流定理)
		for(u=t;u!=s;u=pre[u])
		{
			f[pre[u]][u]+=p[t];      //更新正向流量 
			f[u][pre[u]]-=p[t];      //更新反向流量 
		}
		sum+=p[t];     //更新从s流出的总流量  
	}
	return sum;
}

int main ()
{
	int i;
	int a,b,c;
	while (~scanf("%d%d",&n,&m))
	{
		memset(cap,0,sizeof(cap));
		for(i=0;i<n;i++)
		{
			scanf("%d%d%d",&a,&b,&c);
			cap[a][b]+=c;        //输入可能相同的边
		}
		printf("%d\n",EK(1,m));   
	}
	return 0;
}
</strong></span>


2.预流推进算法(Dinic算法) 

步骤0:构造初始预流flow。
            对源顶点s的每条出边(s,v),令flow(s,v)=cap(s,v);
            对其余边(u,v),令flow(u,v)=0。构造一有效的高度函数h。
步骤1:如果残量网络中不存在活顶点,则计算结束,已经得到最大流.否则转步骤2。
步骤2:在网络中选取活顶点v。
            如果存在顶点v的出边为可推流边,则选取一条这样的可推流边,并沿此边推流。
            否则,令h(v) = min{h(w)+1 | (v,w)是当前残流网络中的边},并转步骤1。

算法实现:
<span style="font-size:18px;"><strong>#include <cstdio>  
#include <cstring>  
#include <queue>  
#define MAXN 205  
#define INF 1000000000  
using namespace std;  
struct Edge {  
    int from, to, cap, flow;  
};  
  
struct Dinic {  
    int n, m, s, t;  
    vector<Edge> edges; //边表.edges[e]和edges[e^1]互为反向弧  
    vector<int> G[MAXN]; //邻接表,G[i][j]表示结点i的第j条边在e数组中的序号  
    bool vis[MAXN]; //BFS使用  
    int d[MAXN];  //从起点到i的距离  
    int cur[MAXN]; //当前弧指针  
  
    void ClearAll(int n) {  
        for (int i = 0; i < n; i++) G[i].clear();  
        edges.clear();  
    }  
  
    void AddEdge(int from, int to, int cap) {  
        edges.push_back((Edge) {from, to, cap, 0});  
        edges.push_back((Edge) {to, from, 0, 0});  
        m = edges.size();  
        G[from].push_back(m - 2);  
        G[to].push_back(m - 1);  
    }  
  
    bool BFS() {//使用BFS计算出每一个点在残量网络中到t的最短距离d.  
        memset(vis, 0, sizeof(vis));  
        queue<int> Q;  
        Q.push(s);  
        vis[s] = 1;  
        d[s] = 0;  
        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 (!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) {//使用DFS从S出发,沿着d值严格递减的顺序进行多路增广。  
        if (x == t || a == 0) return a;  
        int flow = 0, f;  
        for (int& i = cur[x]; i < G[x].size(); 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 Maxflow(int s, int t) {  
        this->s = s; this->t = t;  
        int flow = 0;  
        while (BFS()) {  
            memset(cur, 0, sizeof(cur));  
            flow += DFS(s, INF);  
        }  
        return flow;  
    }  
  
};  
Dinic g;  
int main()  
{  
    int n, m, i, a, b, c;  
    while (~scanf("%d%d", &m, &n)) {  
        g.ClearAll(n + 1);  
        for (i = 0; i < m; i++) {  
            scanf("%d%d%d", &a, &b, &c);  
            g.AddEdge(a, b, c);  
        }  
        int flow = g.Maxflow(1, n);  
        printf("%d\n", flow);  
    }  
    return 0;  
}  </strong></span>



二.最小费用最大流
  求解最小费用流的步骤和求最大流的步骤几乎完全一致,只是在步骤1时选一条非饱和路时,应选代价和最小   的路,即最短路。
  步骤1. 选定一条总的单位费用最小的路,即要给定最小费用的初始可行流,而不是包含边数最小的路。
  步骤2. 不断重复求最大流的步骤来进行,直到没有饱和路存在为止。然后计算每个路的总费用。


算法实现
#include <iostream>    
#include <queue>    
using namespace std;    
#define INF 1000000    
const int MAXN=1000;  
  
/*********************************/    
* 最小割最大流算法    
* 可以在求得最大流的同时获取最小割集S,T    
* 邻接矩阵存储各类信息    
/*********************************/    
    
int cap[MAXN][MAXN]; //容量 ,没有边为0    
int flow[MAXN][MAXN]; //流量    
int cost[MAXN][MAXN]; //耗费矩阵    
int p[MAXN]; //增广路前驱    
int d[MAXN]; //s-t路径最小耗费    
bool inq[MAXN]; //队列标记    
int n; // 顶点数目    
int f; //最大流    
int s,t; //源点,汇点    
int c; //最大流下最小耗费    
    
/*********************************/    
*  邻接矩阵读入图数据    
*  例子:    
4    
0 2 1 0    
0 0 1 1    
0 0 0 1    
0 0 0 0    
    
0 2 5 0    
-2 0 2 3    
-5 -2 0 1    
0 -3 -1 0    
    
注明: 费用矩阵是对称的,有i,j的费用,则j,i费用为其相反数    
/*********************************/    
void read()    
{    
    cin>>n;    
    for(int i=0;i<n;++i)    
    {    
        for(int j=0;j<n;++j)    
        {    
            cin>>cap[i][j];    
        }    
    }    
    for(int i=0;i<n;++i)    
    {    
        for(int j=0;j<n;++j)    
        {    
            cin>>cost[i][j];    
        }    
    }    
}    
    
/*********************************/    
*  void minFlow()    
*  最小费用最大流算法    
*  SPFA()算法找最小耗费增广路    
*  SPFA()其实是Bellman的一个小变形    
*  求得的f一定是最大流,求得的c是最大流下最小耗费    
/*********************************/    
    
void minFlow()    
{    
    cin>>s>>t;    
    queue<int> q;    
    memset(flow,0,sizeof(flow));    
    c=f=0;    
    while(1)    
    {    
        memset(inq,0,sizeof(inq));    
        for(int i=0;i<n;++i)    
        {    
            d[i]= i==s ? 0:INF;    
        }    
        q.push(s);    
        while(!q.empty())    
        {    
            int u=q.front(); q.pop();    
            inq[u]=false;    
            for(int v=0;v<n;v++)    
            {    
                if(cap[u][v]-flow[u][v]>0&&d[v]>d[u]+cost[u][v])    
                {    
                    d[v]=d[u]+cost[u][v];    
                    p[v]=u;    
                    if(!inq[v])    
                    {    
                        inq[v]=true;    
                        q.push(v);    
                    }    
                }    
            }    
        }    
    
        if(d[t]==INF)    
        {    
            cout<<"最大流:"<<f<<",最小耗费:"<<c<<endl;    
            break;    
        }    
        int a=INF;    
        for(int u=t;u!=s;u=p[u])    
        {    
            if(cap[p[u]][u]-flow[p[u]][u]<a)    
            {    
                a=cap[p[u]][u]-flow[p[u]][u];    
            }    
        }    
        for(int u=t;u!=s;u=p[u])    
        {    
            flow[p[u]][u]+=a;    
            flow[u][p[u]]-=a;    
        }    
        c+=d[t]*a;    
        f+=a;    
    }    
}    
    
int main()    
{    
    readG();    
    minCmaxF();    
    return 0;    
}    


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值