网络流

来源:互联网 发布:php就业发展前景 编辑:程序博客网 时间:2024/06/17 00:38

网络流

一.最大流问题
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 506using 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;    }    


0 0
原创粉丝点击