ZJOI2009——狼和羊的故事(最小割最大流定理)

本文介绍了一个关于狼和羊共存问题的算法挑战,通过构建最小割模型实现狼与羊的有效隔离,确保各自领地的独立性。文章详细展示了如何利用图论中的最小割算法解决这一问题,并提供了完整的C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1412: [ZJOI2009]狼和羊的故事

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 732  Solved: 406
[Submit][Status][Discuss]
Description
“狼爱上羊啊爱的疯狂,谁让他们真爱了一场;狼爱上羊啊并不荒唐,他们说有爱就有方向......” Orez听到这首歌,心想:狼和羊如此和谐,为什么不尝试羊狼合养呢?说干就干! Orez的羊狼圈可以看作一个n*m个矩阵格子,这个矩阵的边缘已经装上了篱笆。可是Drake很快发现狼再怎么也是狼,它们总是对羊垂涎三尺,那首歌只不过是一个动人的传说而已。所以Orez决定在羊狼圈中再加入一些篱笆,还是要将羊狼分开来养。 通过仔细观察,Orez发现狼和羊都有属于自己领地,若狼和羊们不能呆在自己的领地,那它们就会变得非常暴躁,不利于他们的成长。 Orez想要添加篱笆的尽可能的短。当然这个篱笆首先得保证不能改变狼羊的所属领地,再就是篱笆必须修筑完整,也就是说必须修建在单位格子的边界上并且不能只修建一部分。
Input
文件的第一行包含两个整数n和m。接下来n行每行m个整数,1表示该格子属于狼的领地,2表示属于羊的领地,0表示该格子不是任何一只动物的领地。
Output
文件中仅包含一个整数ans,代表篱笆的最短长度。
Sample Input
2 2
2 2 
1 1 

Sample Output
2

数据范围
10%的数据  n,m≤3
30%的数据   n,m≤20
100%的数据  n,m≤100

解析:

          很容易看出是最小割。。。因为要将狼和羊分开

          狼连原点,边权inf,羊连汇点,边权inf。。。

          中间,狼到空地,狼到羊,空地到羊,连边,边权为1.。。。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define inf 1000000000

const int dx[4]={0,1,0,-1};
const int dy[4]={1,0,-1,0};
int n,m,s,t,map[110][110],l=0;
struct node
{
    int u,v,c,next;
}edge[100000];
int head[100000];
int d[10010],sumd[10010];

void read()
{
    freopen("wolf.in","r",stdin);
    freopen("wolf.out","w",stdout);
}

void add(int u,int v,int c)
{
  //  printf("%d %d %d\n",u,v,c);
    edge[l].u=u;
    edge[l].v=v;
    edge[l].c=c;
    edge[l].next=head[u];
    head[u]=l++;
    edge[l].u=v;
    edge[l].v=u;
    edge[l].c=0;
    edge[l].next=head[v];
    head[v]=l++;
}

int sap(int u,int flow)
{
    if(u==t)return flow;
    int sum=0;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(edge[i].c && d[u]==d[v]+1)
        {
            int t=sap(v,min(edge[i].c,flow-sum));
            sum+=t;
            edge[i].c-=t;edge[i^1].c+=t;
            if(sum==flow)return sum;
            if(d[0]>=t)return sum;
        }
    }
    sumd[d[u]]--;
    if(sumd[d[u]]==0)d[0]=t+1;
    ++sumd[++d[u]];
    return sum;
}

bool check(int x,int y)
{
    if(1<=x&&x<=n&&y>=1&&y<=m)return 1; else return 0;
}

void work()
{
    scanf("%d%d",&n,&m);
    s=0;t=n*m+1;
    memset(head,-1,sizeof(head));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&map[i][j]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            int cnt=(i-1)*m+j;
            if(map[i][j]==1)
            {
                add(s,cnt,inf);
                for(int k=0;k<4;k++)
                {
                    int newx=i+dx[k];
                    int newy=j+dy[k];
                    if(check(newx,newy))
                    {
                        int nuk=(newx-1)*m+newy;
                        if(map[newx][newy]!=1)add(cnt,nuk,1);
                    }
                }
            }
            if(map[i][j]==2)add(cnt,t,inf);
            if(map[i][j]==0)
            {
                for(int k=0;k<4;k++)
                {
                    int newx=i+dx[k];
                    int newy=j+dy[k];
                    if(check(newx,newy))
                    {
                        int nuk=(newx-1)*m+newy;
                        if(map[newx][newy]!=1)add(cnt,nuk,1);
                    }
                }
            }
        }
    int ans=0;
    sumd[0]=t+1;
    while(d[0]<=t)
        ans+=sap(0,inf);
    printf("%d\n",ans);
}

int main()
{
    read();
    work();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值