zoukankan      html  css  js  c++  java
  • BZOJ 1001

    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1001

    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
    HINT
    2015.4.16新加数据一组,可能会卡掉从前可以过的程序。

    题解:

    无向边转成正反两条有向边,上Dinic求最大流。

    vector存图会MLE,改用链式前向星就好了。

    AC代码:

    #include<bits/stdc++.h>
    #define idx(i,j) ((i-1)*m+j)
    using namespace std;
    const int INF=0x3f3f3f3f;
    const int maxn=1000*1000+10;
    const int maxm=6*maxn;
    
    int n,m,w;
    
    struct Edge{
        int u,v,c,f;
        int next;
    };
    struct Dinic
    {
        int s,t; //源点汇点
        Edge E[maxm];
        int head[maxn],ne;
        void init()
        {
            ne=0;
            memset(head,0,sizeof(head));
        }
        void addedge(int u,int v,int c)
        {
            ++ne;
            E[ne].u=u, E[ne].v=v, E[ne].c=c, E[ne].f=0;
            E[ne].next=head[u];
            head[u]=ne;
        }
        int dist[maxn],vis[maxn];
        queue<int> q;
        bool bfs() //在残量网络上构造分层图
        {
            memset(vis,0,sizeof(vis));
            while(!q.empty()) q.pop();
            q.push(s);
            dist[s]=0;
            vis[s]=1;
            while(!q.empty())
            {
                int now=q.front(); q.pop();
                for(int i=head[now];i;i=E[i].next)
                {
                    Edge& e=E[i]; int nxt=e.v;
                    if(!vis[nxt] && e.c>e.f)
                    {
                        dist[nxt]=dist[now]+1;
                        q.push(nxt);
                        vis[nxt]=1;
                    }
                }
            }
            return vis[t];
        }
        int dfs(int now,int flow)
        {
            if(now==t || flow==0) return flow;
            int rest=flow,k;
            for(int i=head[now];rest>0 && i;i=E[i].next)
            {
                Edge &e=E[i]; int nxt=e.v;
                if(e.c>e.f && dist[nxt]==dist[now]+1)
                {
                    k=dfs(nxt,min(rest,e.c-e.f));
                    if(!k) dist[nxt]=0; //剪枝,去掉增广完毕的点
                    e.f+=k; E[i^1].f-=k;
                    rest-=k;
                }
            }
            return flow-rest;
        }
        int mf; //存储最大流
        int maxflow()
        {
            mf=0;
            int flow=0;
            while(bfs()) while(flow=dfs(s,INF)) mf+=flow;
            return mf;
        }
    }dinic;
    
    int main()
    {
        cin>>n>>m;
        dinic.init();
        dinic.s=idx(1,1);
        dinic.t=idx(n,m);
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<m;j++)
            {
                scanf("%d",&w);
                dinic.addedge(idx(i,j),idx(i,j+1),w);
                dinic.addedge(idx(i,j+1),idx(i,j),w);
            }
        }
        for(int i=1;i<n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                scanf("%d",&w);
                dinic.addedge(idx(i,j),idx(i+1,j),w);
                dinic.addedge(idx(i+1,j),idx(i,j),w);
            }
        }
        for(int i=1;i<n;i++)
        {
            for(int j=1;j<m;j++)
            {
                scanf("%d",&w);
                dinic.addedge(idx(i,j),idx(i+1,j+1),w);
                dinic.addedge(idx(i+1,j+1),idx(i,j),w);
            }
        }
        cout<<dinic.maxflow()<<endl;
    }

    题解2:

    首先在起点 $s$ 和终点 $t$ 之间再连一条直接相连的边,并且画出对偶图,如图:

    将上图中标记删除的边删除,同时删掉之前添加的直接接连接起点和终点的边,得到如下图:

    不难可以看出,从 $S$ 到 $T$ 的任意一条简单路径,都对应原图的一个 $s-t$ 割。而相应地,$s-t$ 最小割就对应着 $S$ 和 $T$ 间的最短路。

    (当然,需要注意的一点是,在 $n=1$ 或者 $m=1$ 时对偶图比较特殊,需要特判一下)

    AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef pair<int,int> pii;
    const int INF=0x3f3f3f3f;
    const int maxn=2*999*999+5;
    const int maxm=4*maxn;
    
    int n,m,w;
    
    struct Edge{
        int v,w;
        int next;
    };
    Edge E[maxm];
    int head[maxn],ne;
    void init()
    {
        ne=0;
        memset(head,0,sizeof(head));
    }
    void addedge(int u,int v,int w)
    {
        ++ne;
        E[ne].v=v, E[ne].w=w;
        E[ne].next=head[u];
        head[u]=ne;
    }
    
    int dist[maxn];
    bool vis[maxn];
    void spfa(int s)
    {
        memset(dist,INF,sizeof(dist)); dist[s]=0;
        memset(vis,0,sizeof(vis));
    
        queue<int> Q;
        Q.push(s), vis[s]=1;
        while(!Q.empty())
        {
            int u=Q.front(); Q.pop(); vis[u]=0;
            for(int i=head[u];i;i=E[i].next)
            {
                int v=E[i].v;
                if(dist[v]>dist[u]+E[i].w)
                {
                    dist[v]=dist[u]+E[i].w;
                    if(!vis[v]) Q.push(v), vis[v]=1;
                }
            }
        }
    }
    
    int main()
    {
        cin>>n>>m;
        init();
        int S=0, T=2*(n-1)*(m-1)+1;
        int rowmin=INF, colmin=INF;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<m;j++)
            {
                scanf("%d",&w);
                rowmin=min(rowmin,w);
                if(i==1) addedge(S,j,w), addedge(j,S,w);
                else if(i==n)
                {
                    int u=(2*i-3)*(m-1)+j;
                    addedge(u,T,w), addedge(T,u,w);
                }
                else
                {
                    int u=(2*i-3)*(m-1)+j, v=(2*i-2)*(m-1)+j;
                    addedge(u,v,w), addedge(v,u,w);
                }
            }
        }
        for(int i=1;i<n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                scanf("%d",&w);
                colmin=min(colmin,w);
                if(j==1)
                {
                    int u=(2*i-1)*(m-1)+j;
                    addedge(u,T,w), addedge(T,u,w);
                }
                else if(j==m)
                {
                    int v=(2*i-2)*(m-1)+(j-1);
                    addedge(S,v,w), addedge(v,S,w);
                }
                else
                {
                    int u=(2*i-2)*(m-1)+(j-1), v=(2*i-1)*(m-1)+j;
                    addedge(u,v,w), addedge(v,u,w);
                }
            }
        }
        for(int i=1;i<n;i++)
        {
            for(int j=1;j<m;j++)
            {
                scanf("%d",&w);
                int u=(2*i-2)*(m-1)+j, v=(2*i-1)*(m-1)+j;
                addedge(u,v,w), addedge(v,u,w);
            }
        }
    
        if(n==1 && m==1) cout<<0<<endl;
        else if(m==1) cout<<colmin<<endl;
        else if(n==1) cout<<rowmin<<endl;
        else
        {
            spfa(S);
            cout<<dist[T]<<endl;
        }
    }
  • 相关阅读:
    hdu 1455 N个短木棒 拼成长度相等的几根长木棒 (DFS)
    hdu 1181 以b开头m结尾的咒语 (DFS)
    hdu 1258 从n个数中找和为t的组合 (DFS)
    hdu 4707 仓鼠 记录深度 (BFS)
    LightOJ 1140 How Many Zeroes? (数位DP)
    HDU 3709 Balanced Number (数位DP)
    HDU 3652 B-number (数位DP)
    HDU 5900 QSC and Master (区间DP)
    HDU 5901 Count primes (模板题)
    CodeForces 712C Memory and De-Evolution (贪心+暴力)
  • 原文地址:https://www.cnblogs.com/dilthey/p/9824302.html
Copyright © 2011-2022 走看看