• 洛谷 P2598 [ZJOI2009]狼和羊的故事 解题报告


    P2598 [ZJOI2009]狼和羊的故事

    题目描述

    “狼爱上羊啊爱的疯狂,谁让他们真爱了一场;狼爱上羊啊并不荒唐,他们说有爱就有方向......” (Orez)听到这首歌,心想:狼和羊如此和谐,为什么不尝试羊狼合养呢?说干就干! (Orez)的羊狼圈可以看作一个(n*m)个矩阵格子,这个矩阵的边缘已经装上了篱笆。可是(Drake)很快发现狼再怎么也是狼,它们总是对羊垂涎三尺,那首歌只不过是一个动人的传说而已。所以(Orez)决定在羊狼圈中再加入一些篱笆,还是要将羊狼分开来养。 通过仔细观察,(Orez)发现狼和羊都有属于自己领地,若狼和羊们不能呆在自己的领地,那它们就会变得非常暴躁,不利于他们的成长。(Orez)想要添加篱笆的尽可能的短。当然这个篱笆首先得保证不能改变狼羊的所属领地,再就是篱笆必须修筑完整,也就是说必须修建在单位格子的边界上并且不能只修建一部分。

    输入输出格式

    输入格式:

    文件的第一行包含两个整数(n)(m)。接下来(n)行每行(m)个整数,1表示该格子属于狼的领地,2表示属于羊的领地,0表示该格子不是任何一只动物的领地。

    输出格式:

    文件中仅包含一个整数(ans),代表篱笆的最短长度。

    说明

    数据范围

    10%的数据 n,m≤3

    30%的数据 n,m≤20

    100%的数据 n,m≤100


    其实如果最小割的题做了不少以后,可以看出来是最小割。

    把狼连(S),羊连(T),边权置(inf),表示归属关系,与种族的关系不可断裂。

    在网格相邻位置的狼羊,狼连羊连单向边权值1,表示必须要断。

    对于0如何处理呢?我们发现,0可以被划分到(S)(T)任意的集合中去。换而言之,0可以跟它在网格中所接触的点随便连边,只要它需要。

    这样转换到图中就是直接把0直接和周围的四个点连出去即可。

    再次分析一下,连出去的边有没有可以不连的?有,比如狼连0和0连狼的边,割掉同样是代表划分到(T)集合,可以只连一条。

    但是0和0就不行,因为0是没有归属的,一个0到另一个0和它的反向边分别被割掉,代表的意义可能就不一样了,要依据其中某一个0本来的归属来定。

    总结:从原无向图建模成最小割问题,一定要注意边的方向所代表的意义


    Code:(这是我一开始写的DINIC,可以感受一下到底有多慢)

    #include <cstdio>
    #include <queue>
    #include <cstring>
    using namespace std;
    const int N=10002;
    const int M=200010;
    const int inf=0x3f3f3f3f;
    const int X[5]={0,0,1,0,-1};
    const int Y[5]={0,-1,0,1,0};
    int head[N],to[M],edge[M],next[M],cnt=1;
    void add(int u,int v,int w)
    {
        edge[++cnt]=w;next[cnt]=head[u];to[cnt]=v;head[u]=cnt;
    }
    int g[N][N],n,m,t;
    int get(int i,int j)
    {
        return m*(i-1)+j;
    }
    void build()
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {
                int id=get(i,j);
                if(g[i][j]==2)
                {
                    add(id,t,inf);
                    add(t,id,0);
                }
                else
                {
                    if(g[i][j]==1)
                    {
                        add(0,id,inf);
                        add(id,0,0);
                    }
                    int v;
                    for(int k=1;k<=4;k++)
                    {
                        if(X[k]+j==0||X[k]+j>m||Y[k]+i==0||Y[k]+i>n)
                            continue;
                        v=get(Y[k]+i,X[k]+j);
                        if(g[Y[k]+i][X[k]+j]!=1)
                        {
                            add(id,v,1);
                            add(v,id,0);
                        }
                    }
                }
            }
    }
    void init()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",&g[i][j]);
        t=n*m+1;
        build();
    }
    int dep[N];
    bool bfs()
    {
        queue <int> q;
        memset(dep,0,sizeof(dep));
        dep[0]=1;
        q.push(0);
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            for(int i=head[u];i;i=next[i])
            {
                int v=to[i],w=edge[i];
                if(!dep[v]&&w)
                {
                    dep[v]=dep[u]+1;
                    if(v==t) return 1;
                    q.push(v);
                }
            }
        }
        return 0;
    }
    int s[N],tot,pre[N],used[N];
    int dfs()
    {
        memset(used,0,sizeof(used));
        s[++tot]=0;int tmp=0;
        while(tot)
        {
            int u=s[tot];
            if(u==t)
            {
                int mi=inf,id;
                for(int i=tot;i>1;i--)
                    if(mi>=edge[pre[s[i]]])
                    {
                        mi=edge[pre[s[i]]];
                        id=i-1;
                    }
                tmp+=mi;
                for(int i=tot;i>1;i--)
                {
                    edge[pre[s[i]]]-=mi;
                    edge[pre[s[i]]^1]+=mi;
                }
                tot=id;
                used[t]=0;
            }
            else
            {
                for(int i=head[u];i;i=next[i])
                {
                    int v=to[i],w=edge[i];
                    if(w&&dep[v]==dep[u]+1&&!used[v])
                    {
                        used[v]=1;
                        pre[v]=i;
                        s[++tot]=v;
                        break;
                    }
                }
                if(u==s[tot]) tot--;
            }
        }
        return tmp;
    }
    void work()
    {
        int maxflow=0;
        while(bfs())
            maxflow+=dfs();
        printf("%d
    ",maxflow);
    }
    int main()
    {
        init();
        work();
        return 0;
    }
    
    

    2018.7.1

  • 相关阅读:
    CodeSmith注册错误的解决方法
    我是“坚守者”还是"背叛者"?
    拿什么留住你,我的程序员
    去除HTML代码得函数
    页面之间传递参数得几种方法
    nhibernate source code analyzed (abstract classes in nhibernate2.0)
    Web 2.0时代RSS的.Net实现
    Visual Studio.net 2003安装提示重启问题
    开放思路,综合考虑,心胸开阔,做一个合格的项目经理
    了解实际开发中 Hashtable 的特性原理 .NET, JAVA, PHP
  • 原文地址:https://www.cnblogs.com/butterflydew/p/9251540.html
Copyright © 2020-2023  润新知