[网络流]hdu3046(Dinic)

首先我想讲一下网络流的基础。
第一,要明白一些基础的概念,否则对后面的理解会有一定的影响。这里只列出常用的,列太多会影响阅读效率的。

底图:如果把一个有向图的每条边的方向都去掉,得到的无向图称为原有图的底图。
途径:图G中点边连续交替出现的序列称为G的一条途径。
迹:图G中边不重复出现的途径称为迹。
路:图G中顶点不重复出现的迹称为路。

网络的基本概念:
定义:一个网络N=(V,A)是指一个连通无环且满足下列条件的有向图:
1.有一个定点子集X,其每个顶点的入度都是0;
2.有一个与X不相交的顶点子集Y,其每个顶点的出度都为0;
3.每条弧都有一个非负的权值,称为弧的容量。
上述网络N记作N(V,X,Y,A,C),其中X为网络的源点集,Y称为网络的汇点集,V和A分别称为顶点集和弧集。C为网络的容量函数。

网络的可行流:
网络N=(V,X,Y,A,C)中的一个可行流是指定义在A上的一个整值函数f,使得:
1.对任意a属于A,0<=f(a)<=c(a)(容量约束);
2.对任意v属于 V-(XUY),f(v)=f’(v)(流量守恒);
其中f(v)表示点v的入弧的流量之和,f’(v)表示点v的出弧的流量之和。

最大流最小割定理:在任一网络中,最大流的流量等于最小割的容量。
此定理对于如何求最大流并没有什么用处,但是可以用他来证明最大流的正确性。

最大流的求解:
求网络中的最大流基本思想都是不断地增加流量,直到不能再增加为止。这就引入了可增路的概念。
:设u,v是网络N中任意两点,P是N的底图中的一条连接uv的路,若规定P的走向为从u到v,则称这样规定了走向的路P为网络N中一条从u到v的路,简称u-v路。特别的,一条从源点x到汇点y的路称为x-y路。
正向弧就是路中的边在原图中是否是正向弧。不是就是反向弧。
可增路:假设f是网络的一个可行流,u是N中任意一点,P是网络N中的一条x-u路,如果对路P上的任一弧a,都有:
1.若弧a是P的正向弧,则c(a)-f(a)>0;
2.若弧a是P的反向弧,则f(a)>0;
则P称为N的一条可增x-u路。特别的,一条f可增x-y路称为 N的一条f可增路。

对于N中任意一条f可增路P和P上任意一条弧a,假设
△f(a)= c(a)-f(a) (正向弧)
f(a) (反向弧)
则可增路的可增加流量为△f(P)=min{△f(a)};称为可增量。
这里写图片描述

最大流Dinic算法:

Dinic算法利用分层的思想对网络进行一些处理,简化了一些操作。
1.增量网络(残余网络)

对于网络N和N上的可行流f,构造一个新的网络N(f)=(V,X,Y,A(f),C’)中A(f)及容量函数C’定义如下:

(为何这么诡异,把这句好敲出来博客页面就显示不出下面的两张图和部分文字。。无奈,只好截图了。。。噗噗噗。。)

2.若(u,v)属于A并且f(u,v)>0,则(v,u)属于A(f),并且c’(u,v)=f(u,v);
分层就是bfs一遍扫描网络,求出每个点到源点的最短距离。
对分层后的网络进行下一步的操作就可以得到辅助网络。
辅助网络:对于增量网络N(f)进行分层后,删除层数不低于y的顶点,再删除从高层指向低层的弧和同层之间的弧。

这里写图片描述

这里写图片描述

其实代码中并没有很清晰的看到辅助网络的身影,直接是bfs分层,dfs计算△f(a),直到bfs不能达到汇点。

好了,说一下本题的题意吧。

题意:就是在一个网格中有些是羊,有两个是灰太狼和红太狼,问怎样用最少的栏杆拦截使小羊安全。

分析:
每个点都有向相邻点的连线,容量为1,狼都连向汇点,羊都连向源点,容量为inf。求最大流。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
#define read freopen("q.in","r",stdin)
#define maxn 40004
#define inf 0x7fffffff
int edgeNum,n,m;
int d[maxn],head[maxn];

struct Edge
{
    int to,flow,next;
}edge[maxn*4];
int from,to;
void add(int u,int v,int flow)
{
    edge[edgeNum].to=v;edge[edgeNum].flow=flow;edge[edgeNum].next=head[u];head[u]=edgeNum++;
    edge[edgeNum].to=u;edge[edgeNum].flow=0;edge[edgeNum].next=head[v];head[v]=edgeNum++;
}

bool bfs()
{
    memset(d,0,sizeof(d));
    int i,k,j;
    queue<int> q;
    d[from]=1;
    q.push(from);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(!d[v] && edge[i].flow>0)
            {
                d[v]=d[u]+1;
                q.push(v);
                if(v==to)return true;
            }
        }
    }
    return false;
}

int dfs(int u,int flow)
{
    if(u==to || flow==0)return flow;
    int i,j,k;
    int cap=flow;
    for(i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(d[v]==d[u]+1 && edge[i].flow>0)
        {
            int x=dfs(v,min(cap,edge[i].flow));
            cap-=x;
            edge[i].flow-=x;
            edge[i^1].flow+=x;
            if(cap==0)return flow;
        }
    }
    return flow-cap;
}

int dinic()
{
    int sum=0;
    while(bfs())sum+=dfs(from,inf);
    return sum;
}
int main()
{
//  read;
    int i,j,x,cas=1;
    while(~scanf("%d%d",&n,&m))
    {
        from=n*m+1;to=n*m+2;
        edgeNum=0;
        memset(head,-1,sizeof(head));
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=m;j++)
            {
                scanf("%d",&x);
                if(x==1)add(from,(i-1)*m+j,inf);
                if(x==2)add((i-1)*m+j,to,inf);
                if(i-1>=1 )add((i-1)*m+j,(i-2)*m+j,1);
                if(j-1>=1 )add((i-1)*m+j,(i-1)*m+j-1,1);
                if(i+1<=n )add((i-1)*m+j,i*m+j,1);
                if(j+1<=m )add((i-1)*m+j,(i-1)*m+j+1,1);

            }
        }
        int res=dinic();
        cout<<"Case "<<cas++<<":\n"<<res<<endl;
    }
}

ps:我觉得讲算法附上代码很重要。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值