[ZJOI2009]狼和羊的故事

Posted 日拱一卒 功不唐捐

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[ZJOI2009]狼和羊的故事相关的知识,希望对你有一定的参考价值。

https://www.luogu.org/problem/show?pid=2598

题目描述

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

输入输出格式

输入格式:

 

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

 

输出格式:

 

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

 

输入输出样例

输入样例#1:
2 2
2 2 
1 1 
输出样例#1:
2

说明

数据范围

10%的数据 n,m≤3

30%的数据 n,m≤20

100%的数据 n,m≤100

 

源点向羊连流量为inf的边,狼向汇点连流量为inf的边

羊向相邻的狼连流量为1的边

羊向相邻的空地连流量为1的边

狼向相邻的空地连流量为1的边

空地之间连流量为1的边

相邻指上下左右,均是单向边

空地之间需要双向边,但两块两邻空地会枚举两边,所以相当于连了双向边

最小割就是答案

 

#include<queue>
#include<cstdio>
#define N 101

using namespace std;

const int inf=2e9;

int n,m,a[N][N];
int src,decc,ans;
int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};
int front[N*N],to[N*N*10],nxt[N*N*10],cap[N*N*10],tot=1;
int cnt[N*N],lev[N*N];

queue<int>q;

void add(int u,int v,int w)
{
    to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; cap[tot]=w;
    to[++tot]=u; nxt[tot]=front[v]; front[v]=tot; cap[tot]=0;
}
int turn(int i,int j)
{
    return (i-1)*m+j;
}
bool bfs()
{
    for(int i=src;i<=decc;i++) lev[i]=-1,cnt[i]=front[i];
    while(!q.empty()) q.pop();
    q.push(src); lev[src]=0;
    int now;
    while(!q.empty())
    {
        now=q.front(); q.pop();
        for(int i=cnt[now];i;i=nxt[i])
            if(lev[to[i]]==-1 && cap[i])
            {
                lev[to[i]]=lev[now]+1;
                q.push(to[i]);
                if(to[i]==decc) return true; 
            }
    }
    return false;
}
int dinic(int now,int flow)
{
    if(now==decc) return flow;
    int delta,rest=0;
    for(int &i=cnt[now];i;i=nxt[i])
    {
        if(cap[i] && lev[to[i]]>lev[now])
        {
            delta=dinic(to[i],min(flow-rest,cap[i]));
            if(delta) 
            {
                rest+=delta;
                cap[i]-=delta; cap[i^1]+=delta;
                if(rest==flow) break;
            }
        }
    }
    if(rest!=flow) lev[now]=-1;
    return rest;
}
int main()
{
    scanf("%d%d",&n,&m);
    decc=n*m+1;
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++)
      {
           scanf("%d",&a[i][j]);
           if(a[i][j]==2) add(src,turn(i,j),inf);
         else if(a[i][j]==1) add(turn(i,j),decc,inf);  
      }
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++)
     {
        if(a[i][j]==1)
         {
             for(int k=0;k<4;k++)
             if(i+dx[k]>0 && i+dx[k]<=n && j+dy[k]>0 && j+dy[k]<=m && !a[i+dx[k]][j+dy[k]]) 
               add(turn(i+dx[k],j+dy[k]),turn(i,j),1);
         }
        else if(a[i][j]==2)
         {
             for(int k=0;k<4;k++)
             if(i+dx[k]>0 && i+dx[k]<=n && j+dy[k]>0 && j+dy[k]<=m &&a[i+dx[k]][j+dy[k]]!=a[i][j]) 
              add(turn(i,j),turn(i+dx[k],j+dy[k]),1);
         }
        else
         for(int k=0;k<4;k++)
          if(i+dx[k]>0 && i+dx[k]<=n && j+dy[k]>0 && j+dy[k]<=m && !a[i+dx[k]][j+dy[k]]) 
            add(turn(i,j),turn(i+dx[k],j+dy[k]),1);
     }
    while(bfs()) ans+=dinic(src,inf);
    printf("%d",ans);
}

 

以上是关于[ZJOI2009]狼和羊的故事的主要内容,如果未能解决你的问题,请参考以下文章

bzoj1412: [ZJOI2009]狼和羊的故事

BZOJ1412[ZJOI2009]狼和羊的故事 最小割

洛谷 P2598 [ZJOI2009]狼和羊的故事

P2598 [ZJOI2009]狼和羊的故事(最小割)

1412: [ZJOI2009]狼和羊的故事

[ZJOI2009]狼和羊的故事