• 题解 P2598 【[ZJOI2009]狼和羊的故事】


    P2598 [ZJOI2009]狼和羊的故事

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

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

    输出格式:
    文件中仅包含一个整数ans,代表篱笆的最短长度。


    分析

    要保证篱笆长最小而把狼和羊分开来,我们可以联想到最小割模型。一个图的最小割就是把图分为两个部分(源点及汇点不在同一部)的边权和。最小割可以用最大流算法求得。

    建模

    要说到网络流,重点就在于建模了,我们怎么把此网格图转换为最大流网络流呢?其实对于一个格子,我们可以把它看做与上下左右四个方向都有一条连边,而把这个格子抽象成一个点,如下图:

    依据题意和最大流的经验,我们可以连边了:(我以羊的一部作为源点,所以)源点连羊,狼连汇点,若相邻的点事狼,则连一条容量为1的边(他的模型意义是:把羊和狼分开【割】需要消耗“1”)

    但是对于0怎么办呢?这是本题的难点

    可以思索一下,若是把0全部归为狼或者羊吧,感觉又会有更优解(事实也是这样,因为狼和羊是等价的【把狼从羊中隔离开来等价于把羊从狼中隔离开来】,所以这样单方面划分是肯定不正确的),那么怎么办呢

    你可能不会,但你的最大流算法一定知道怎么做

    我们这样连:源点---羊--(边A,c=1)--0--(边B, c=1)--狼---汇点

    试想一下,你的篱笆的作用是分割狼和羊,0这些格子要么被划分到狼的领地,要么被划分到羊的领地,若是划分到狼那边,你的算法会割开靠近羊的那条边 A ,要是划分到羊这边,他会自动割开靠近狼的边 B 。一定不存在一种割的方式,使 A 和 B 同时被割开,因为你的算法知道,割一条就足以分开两点,不需要割第二条

    所以,放手给程序去跑吧

    Code

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    int RD(){
        int out = 0,flag = 1;char c = getchar();
        while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
        while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
        return flag * out;
        }
    const int maxn = 100019,INF = 1e9;
    int nume = 1;
    int lenx,leny;
    int map[190][190];
    int mx[4] = {1,-1,0,0};
    int my[4] = {0,0,1,-1};
    int s,t,maxflow;
    int head[maxn];
    struct Node{
        int v,dis,nxt;
        }E[maxn << 2];
    void add(int u,int v,int dis){
        E[++nume].nxt = head[u];
        E[nume].v = v;
        E[nume].dis = dis;
        head[u] = nume;
        }
    int lev[maxn];
    bool bfs(){
        queue<int>Q;
        memset(lev,0,sizeof(lev));
        Q.push(s);
        lev[s] = 1;
        while(!Q.empty()){
            int u = Q.front();
            Q.pop();
            for(int i = head[u];i;i = E[i].nxt){
                int v = E[i].v;
                if(E[i].dis && !lev[v]){
                    lev[v] = lev[u] + 1;
                    Q.push(v);
                    if(v == t)return 1;
                    }
                }
            }
        return 0;
        }
    int Dinic(int u,int flow){
        if(u == t)return flow;
        int rest = flow,k;
        for(int i = head[u];i;i = E[i].nxt){
            int v = E[i].v;
            if(E[i].dis && lev[v] == lev[u] + 1 && rest){
                k = Dinic(v,min(rest,E[i].dis));
                if(!k)lev[v] = 0;
                E[i].dis -= k;
                E[i ^ 1].dis += k;
                rest -= k;
                }
    
            }
        return flow - rest;
        }
    int getindex(int x,int y){
        return (x - 1) * leny + y;
        }
    bool judge(int x,int y){
        if(x < 1 || x > lenx || y < 1 || y > leny)return 0;
        return 1;
        }
    /*for(int i = 1;i <= lenx;i++){
            for(int j = 1;j <= leny;j++){
                
                }
            }*/
    void build(){
        for(int i = 1;i <= lenx;i++){
            for(int j = 1;j <= leny;j++){
                if(map[i][j] == 2){
                    add(s,getindex(i,j),INF);
                    add(getindex(i,j),s,0);
                    }
                else if(map[i][j] == 1){
                    add(getindex(i,j),t,INF);
                    add(t,getindex(i,j),0);
                    }
                }
            }
        for(int i = 1;i <= lenx;i++){
            for(int j = 1;j <= leny;j++){
                if(map[i][j] == 2 || map[i][j] == 0){
                    for(int k = 0;k < 4;k++){
                        int nx = i + mx[k],ny = j + my[k];
                        if(!judge(nx,ny))continue;
                        if(map[nx][ny] == 1 || map[nx][ny] == 0){
                            add(getindex(i,j),getindex(nx,ny),1);
                            add(getindex(nx,ny),getindex(i,j),0);
                            }
                        }
                    }
                }
            }
        }
    int main(){
        lenx = RD();leny = RD();
        for(int i = 1;i <= lenx;i++){
            for(int j = 1;j <= leny;j++){
                map[i][j] = RD();
                }
            }
        s = lenx * leny + 1,t = s + 1;
        build();
        int flow = 0;
        while(bfs())while(flow = Dinic(s,INF))maxflow += flow;
        printf("%d
    ",maxflow);
        return 0;
        }
    
  • 相关阅读:
    NS网络仿真,小白起步版,双节点之间的模拟仿真(基于UDP和CBR流)
    Linux学习,ACL权限管理
    SQL中的注释语句
    C#连接SQL Server数据库小贴士
    C#重写ToString
    C#控制台应用程序之选课系统
    浅谈C、C++及其区别、兼容与不兼容
    C++之客户消费积分管理系统
    A*算法
    HTML标签列表总览
  • 原文地址:https://www.cnblogs.com/Tony-Double-Sky/p/9285535.html
Copyright © 2020-2023  润新知