zoukankan      html  css  js  c++  java
  • 【BZOJ】1001: [BeiJing2006]狼抓兔子 Dinic算法求解平面图对偶图-最小割

    1001: [BeiJing2006]狼抓兔子

    Description

     

    左上角点为(1,1),右下角点为(N,M)(上图中N=4,M=5).有以下 三种类型的道路 1:(x,y)<==>(x+1,y) 2:(x,y)<==>(x,y+1) 3:(x,y)<==>(x+1,y+1) 道路上的权值表示这条路上最多能够通过的兔子数,道路是无向的. 左上角和右下角为兔子的两个窝,开始时所有的兔子都聚集在左上角(1,1)的窝里,现在它们要跑到右下角(N,M)的窝中去,狼王开始伏击这些兔子.当然 为了保险起见,如果一条道路上最多通过的兔子数为K,狼王需要安排同样数量的K只狼,才能完全封锁这条道路,你需要帮助狼王安排一个伏击方案,使得在将兔 子一网打尽的前提下,参与的狼的数量要最小。因为狼还要去找喜羊羊麻烦.

    Input

    第一 行为N,M.表示网格的大小,N,M均小于等于1000.接下来分三部分第一部分共N行,每行M-1个数,表示横向道路的权值. 第二部分共N-1行,每行M个数,表示纵向道路的权值. 第三部分共N-1行,每行M-1个数,表示斜向道路的权值. 输入文件保证不超过10M

    Output

    输出一个整数,表示参与伏击的狼的最小数量.

    Sample Input

    3 4
    5 6 4
    4 3 1
    7 5 3
    5 6 7 8
    8 7 6 5
    5 5 5
    6 6 6

    Sample Output

    14

    讲讲Dinic算法:(Dinic算法属于最短增广路中的一种)

    层次图:每次在残量网络BFS得到每点到起点的距离;路径是在层次中找的,即d[v] == d[x]+1;比EK算法更高效

    优化1:在DFS里面并不是每次只走一条路径,而是DFS到一条最短路之后,在回溯到不含最短边继续搜索;在DFS里面a表示目前为止所有弧的最小残量;而f表示路径的流量;即f<=a;根据a -= f是否等于0来判断是在当前节点几次上继续搜索还是回溯;

    优化2:因为一个点可能会被多次搜索到,所以记录下前面搜索到该节点的那条边的序号,这样就不会从头开始搜索了;

    ps:图中是无向边,我竟然还是建了反向边cap为0的图,真是醉了;JMJST使用Djistra+heap只用了516ms;我也重写了一个对偶图版本的,348ms~~详见 平面图最小割 对偶图

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<string.h>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<vector>
    #include<cmath>
    #include<stdlib.h>
    #include<time.h>
    #include<stack>
    #include<set>
    using namespace std;
    #define rep0(i,l,r) for(int i = (l);i < (r);i++)
    #define rep1(i,l,r) for(int i = (l);i <= (r);i++)
    #define rep_0(i,r,l) for(int i = (r);i > (l);i--)
    #define rep_1(i,r,l) for(int i = (r);i >= (l);i--)
    #define MS0(a) memset(a,0,sizeof(a))
    #define MS1(a) memset(a,-1,sizeof(a))
    #define inf 0x3f3f3f3f
    template<typename T>
    void read1(T &m)
    {
        T x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        m = x*f;
    }
    template<typename T>
    void read2(T &a,T &b){read1(a);read1(b);}
    template<typename T>
    void read3(T &a,T &b,T &c){read1(a);read1(b);read1(c);}
    template<typename T>
    void out(T a)
    {
        if(a>9) out(a/10);
        putchar(a%10+'0');
    }
    const int M = 1000*1010;
    int head[M*6],tot;
    struct edge{
        int from,to,cap,flow,Next;
    }e[M*6];
    void ins(int u,int v,int cap)
    {
        e[tot].Next = head[u];
        e[tot].from = u;//为了t->s时由v推到u;
        e[tot].to = v;
        e[tot].cap = cap;
        e[tot].flow = 0;
        head[u] = tot++;
    }
    int vis[M],s,t,cur[M],d[M];
    queue<int> Q;
    int BFS()
    {
        rep1(i,s,t) vis[i] = 0;
        vis[s] = 1;d[s] = 0;
        Q.push(s);
        while(!Q.empty()){
            int u = Q.front();Q.pop();
            for(int i = head[u];~i;i = e[i].Next){
                int v = e[i].to;
                if(!vis[v] && e[i].cap > e[i].flow){ // 只考虑残量网络的弧               
                    vis[v] = 1;
                    d[v] = d[u] + 1;
                    Q.push(v);
                }
            }
        }
        return vis[t];
    }
    int DFS(int x,int a)// a表示目前为止所有弧的最小残量
    {
        if(x == t || a == 0) return a;
        int& i = cur[x];//回溯时会多次DFS到同一个点
        if(i == 0) i = head[x];
        int flow = 0, f;
        for(;~i;i = e[i].Next){// 从上次考虑的弧开始
            int v = e[i].to;
            if(d[v] == d[x]+1 && (f = DFS(v,min(a,e[i].cap - e[i].flow))) > 0){
                e[i].flow += f;
                e[i^1].flow -= f;
                flow += f;
                a -= f;// 残量-流量
                if(a == 0)   break;
            }
        }
        return flow;
    }
    int Dinic()
    {
        int flow = 0;
        while(BFS()){//仍然存在增广路时再DFS
            rep1(i,s,t) cur[i] = 0;//记录当前探索到的点的弧的编号
            flow += DFS(s,inf);
        }
        return flow;
    }
    void input()
    {
        int n,m,cost;
        read2(n,m);
        s = 0,t = n*m - 1;
        MS1(head);tot = 0;
        rep0(i,0,n){
            rep0(j,0,m-1){
                read1(cost);
                int u = i*m+j;
                ins(u,u+1,cost);ins(u+1,u,cost);
            }
        }
        rep0(i,0,n-1){
            rep0(j,0,m){
                read1(cost);
                int u = i*m+j,v = u + m;
                ins(u,v,cost);ins(v,u,cost);
            }
        }
        rep0(i,0,n-1){
            rep0(j,0,m-1){
                read1(cost);
                int u = i*m+j,v = u + m + 1;
                ins(u,v,cost);ins(v,u,cost);//无向边
            }
        }
    }
    int main()
    {
        //freopen("in.txt","r",stdin);
        //freopen("out.txt","w",stdout);
        input();
        out(Dinic());
        return 0;
    }
    View Code


     

  • 相关阅读:
    UVa-1218
    Uva-1220
    UVa-10003
    UVa-1625
    UVa-11584
    UVa-12563
    UVa-12166 Equilibrium Mobile
    加油
    UVa-10129
    不再刷“水题”!
  • 原文地址:https://www.cnblogs.com/hxer/p/5187633.html
Copyright © 2011-2022 走看看