码迷,mamicode.com
首页 > 其他好文 > 详细

《网络流学习笔记01--HDU3549》

时间:2015-02-10 21:48:40      阅读:328      评论:0      收藏:0      [点我收藏+]

标签:网络流 最大流

1.网络流初步。

网络流是一个适用范围相当广泛的模型,相关的算法也很多,这里就几天学习网络流的相关知识做一个总结归纳。

(1)最大流问题

如图所示,假设你需要把一些物品从结点s(称为源点)运送到结点t(称为汇点),可以从其他结点中转,图(a)中各条有向边的权表示最多能有多少个物品从这条边的起点直接运送到终点,例如图(a)从结点V3到V2最多可以运送9个物品。

图(b)给出了一种可能的最优方案,其中每条边中的第一个数字表示实际运送的物品数量,第二个数字表示题目中的上限,

技术分享技术分享

我们把求解这样的问题称为最大流问题(Maximum-Flow Problem)。对于一条边(u,v)它的物品上限称为容量(capacity),记为c(u,v),(对于不存在的边(u,v),c(u,v)=0);实际运送的物品称为流量(flow),记为f(u,v)。注意:“如果把3个物品从结点u运送到结点v,有把5个物品从v运送到u”等价于把两个物品从v运送到u,

这样,我们就可以定义f(u,v)和f(v,u)最多只有一个正数(可以均为0),并且f(u,v)=-f(v,u)。这样规定就好比“把m个物品从u运送到v“等价于“把”-m个物品从v运送到u”一样。

注意:

  • 最大流问题就好像一个贪心问题,目标是把最多的物品从s(源点)运送到t(汇点)而其他结点都是中转,因此,对于除了结点s和t外的任意结点u,
  • 技术分享(别忘了这些f中有些是负数),从s运送出来的物品数目等于到达t的物品数目,这正是最大化的目标。

在最大流问题中,容量c和流量f满足3个性质:容量限制(f(u,v)<=c(u,v)),斜对称性(f(u,v)=-f(v,u)),流量平衡(对于除了结点s和t外的任意结点u技术分享

即:

技术分享

问题的目标是最大化|f|=技术分享=技术分享即从s点流出的净流量。


1.增广路算法

介绍完最大流问题后,接下来给出求解问题的方法。算法思路也很简单,从零流(所有边的流量为0)开始不断增加流量,保持每次增加流量后都满足容量限制,斜对称性和流量平衡3个条件,

把图(a)中的每条边上容量与流量之差(称为残余容量,简称残量)计算出,得到图(b)的残量网络(rasidual network)。同理,有图(c)可得到图(d),注意残量网络中的边数可能达到原图中的两倍,如原图中c=16,f=11。

技术分享

上述算法基于这样一个事实:残量网络中任何一条从s到t的有向路都对应一条原图中的增广路(augmenting path)-只要求出改条道路中所有残量的最小值d,把对应的所有边上的流量增加d即可,这个过程称为增广(augmenting)。不难验证,如果增广前的流量满足3个条件,增广路仍然满足,显然,只要残量网络中存在增广路,流量就可以增大

同理可以证明它的逆命题也成立:如果残量网络中不存在增广路,则当前流就是最大流,这就是增广路定理。

注意:当且仅当残量网络中不存在s-t的有向路(增广路)时,此时的流是从s到t的最大流。

【BFS实现】

bfs足以应对数据不难的网络流问题,这就是Edmonds-Karp算法,下面的代码中,源点和汇点保存在变量s和t中,运行结束后,s-t的净流量保存在变量f中。

queue <int > Q;
memset(flow,0,sizeof(flow));
int sum=0;
for(;;)
{
    memset(a,0,sizeof(a));
    a[s]=inf;
    Q.push();
    while(!Q.empty())                        //BFS找增广路
    {
        int u=Q.front();Q.pop();
        for(int v=1;v<=n;v++)
            if(!a[v]&&cap[u][v]>flow[u][v])  //找到新结点v
        {
            p[v]=u;Q.push(v);                //记录v的父亲,并且加入FIFO队列
            int ans=cap[u][v]-flow[u][v]; 
            a[v]=a[u]<ans?a[u]:ans;          //s-v路径上的最小残量
        }
    }
    if(a[t]==0) break;                       //找不到,则当前是最大流
    for(int u=t;u!=s;u=p[u])                 //从汇点往回走
    {
        flow[p[u]][u]+=a[t];                 //更新正向流量
        flow[u][p[u]]-=a[t];                 //更新反向流量
    }
    sum+=a[t];                               //更新从s流向的总流量
}


注意:在扩展结点的同时还需要递推出从s到每个结点i的路径上的最小残量a[i],则a[t]就是整条s-t道路上的最小残量,另外,由于a[i]一直是正数,可以用它代替原来的vis标识数组,上面的代码初始化流为零,实际只要满足3个限制条件,初始化可行就可以用增广路算法进行增广。

【HDU 3549】入门裸题

题目链接:click here

#include <math.h>//BFS
#include <queue>
#include <deque>
#include <stack>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
#define Max(a,b) a>b?a:b
#define Min(a,b) a>b?b:a
#define mem(a,b) memset(a,b,sizeof(a))
int dir[4][2]= {{1,0},{-1,0},{0,1},{0,-1}};
const double eps = 1e-6;
const double Pi = acos(-1.0);
const int maxn = 25;
const int inf=0x3f3f3f3f;
int  n,m,i,j;
bool flag[maxn][maxn];
int cap[maxn][maxn];
int flow[maxn][maxn];
int bfs()
{
    int p[50];
    queue<int> Q;
    int sum=0;
    int a[maxn];
    for(;;)
    {
        memset(a,0,sizeof a);
        a[1]=inf;
        Q.push(1);
        while (!Q.empty())
        {
            int u=Q.front();
            Q.pop();
            for (int v=1; v<=n; ++v)
                if (!a[v] && cap[u][v]>flow[u][v])
                {
                    p[v]=u;
                    Q.push(v);
                    int ans=cap[u][v]-flow[u][v];
                    a[v]=a[u]<ans?a[u]:ans;
                }
        }
        if (a[n]==0) break;
        for (int u=n; u!=1; u=p[u])
        {
            flow[p[u]][u]+=a[n];
            flow[u][p[u]]-=a[n];
        }
        sum+=a[n];
    }
    return sum;
}
int main()
{
    int N;
    int cas=0;
    scanf("%d",&N);
    while (N--)
    {
        mem(cap,0),mem(flow,0);
        scanf("%d%d",&n,&m);
        for (int i=0; i<m; ++i)
        {
            int x,y,t;
            scanf("%d%d%d",&x,&y,&t);
            cap[x][y]+=t;
        }
        printf("Case %d: %d\n",++cas,bfs());
    }
    return 0;
} 

#include <ctype.h>  //DFS实现
#include <stdio.h>
#include <vector>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1101;
const int inf=0x3f3f3f3f;
struct edge
{
    int to,cap,rev;
};
vector<edge > G[maxn];        //图的邻接表表示
bool used[maxn];              //访问标记
void add_edge(int from,int to,int cap)
{
    G[from].push_back((edge)
    {
        to,cap,G[to].size()
    });
    G[to].push_back((edge)
    {
        from,0,G[from].size()-1
    });
}
int dfs(int v,int t,int f)
{
    if(v==t) return f;
    used[v]=true;
    for(int i=0; i<G[v].size(); i++)
    {
        edge &e=G[v][i];
        if(!used[e.to]&&e.cap>0)
        {
            int d=dfs(e.to,t,min(f,e.cap));
            if(d>0)
            {
                e.cap-=d;
                G[e.to][e.rev].cap+=d;
                return d;
            }
        }
    }
    return 0;
}
int max_flow(int s,int t)
{
    int flow=0;
    for(;;)
    {
        memset(used,0,sizeof(used));
        int f=dfs(s,t,inf);
        if(f==0) return flow;
        flow+=f;
    }
}
int main()
{
    int n,m,too,capp,revv;
    int tt,j=1;
    scanf("%d",&tt);
    while(tt--)
    {
        scanf("%d%d",&m,&n);
        memset(G,0,sizeof(G));
        for(int i=0; i<n; i++)
        {
            scanf("%d%d%d",&too,&capp,&revv);
            add_edge(too,capp,revv);
        }

        printf("Case %d: %d\n",j++,max_flow(1,m));
    }
    return 0;
}





《网络流学习笔记01--HDU3549》

标签:网络流 最大流

原文地址:http://blog.csdn.net/u013050857/article/details/43699411

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!