【LuoguP2598 [ZJOI2009]】狼和羊的故事

题目链接

题目描述

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

题解

上面题目说了一大堆,简单来说就是让你将一些格子的边界用篱笆围起来,使狼和羊不能走到各自对方的领地。(注意有空地!!!)

新建源点汇点 ,源点向所有羊连容量为INF的边,所有狼向汇点连容量为INF的边(可反)。其他点向周围的点连容量为1的边,但要注意流向,狼只连入边,羊只连出边,这样最大流(最小割更形象)即为最少要修的篱笆数。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#define Set(a,b) memset(a,b,sizeof(a))
#define Copy(a,b) memcpy(a,b,sizeof(a))
using namespace std;
inline int read()
{
    int x=0;char ch=getchar();int t=1;
    for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=-1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
    return x*t;
}
const int N=10100;
const int M=120000;
const int INF=2147483647;
struct edge{
    int to;int next;int cap;
}a[M];
int cur[N];
int head[N];
int dis[N];
#define Get(a,b) (((a)-1)*(m)+(b))
int n,m;
int cnt=0;
int map[103][103];
int tot;
inline void add(int x,int y,int z)
{
    a[cnt]=(edge){y,head[x],z};
    head[x]=cnt++;
}
queue<int> Q;
inline void clear(queue<int>&Q)
{
    queue<int> P;swap(P,Q);
}
inline bool bfs()
{
    clear(Q);
    Set(dis,-1);dis[0]=0;
    Q.push(0);
    while(!Q.empty())
    {
        register int u=Q.front();Q.pop();
        for(register int v,i=head[u];i!=-1;i=a[i].next)
        {
            v=a[i].to;
            if(a[i].cap==0||dis[v]!=-1) continue;
            dis[v]=dis[u]+1;
            if(v==tot) return 1;
            Q.push(v);
        }
    }
    return (dis[tot]!=-1);
}
inline int dfs(int u,int flow)
{
    if(u==tot) return flow;
    register int delta=flow;
    for(register int v,&i=cur[u];i!=-1;i=a[i].next)
    {
        v=a[i].to;
        if(a[i].cap==0||dis[v]!=dis[u]+1) continue;
        register int d=dfs(v,min(delta,a[i].cap));
        delta-=d;a[i].cap-=d;a[i^1].cap+=d;
        if(d==0) dis[v]=0;
        if(delta==0) break;
    }
    return flow-delta;
}
inline void Dinic()
{
    register int max_flow=0;
    while(bfs()) Copy(cur,head),max_flow+=dfs(0,INF);
    printf("%d\n",max_flow);
}
int main()
{
    Set(head,-1);
    n=read();m=read();tot=n*m+1;
    for(register int i=1;i<=n;i++)
        for(register int j=1;j<=m;j++)
        {
            map[i][j]=read()+1;
            if(map[i][j]==2) add(Get(i,j),tot,INF),add(tot,Get(i,j),0);
            else if(map[i][j]==3) add(0,Get(i,j),INF),add(Get(i,j),0,0);
        }
    for(register int i=1;i<=n;i++)
        for(register int j=1;j<=m;j++){//保证流向 即保证一开始 不会有 有容量的边连向羊或由狼连出
            if(map[i][j]==2) continue;
            register int num=Get(i,j);
            if(map[i-1][j]&&map[i-1][j]!=3) add(num,Get(i-1,j),1),add(Get(i-1,j),num,0);
            if(map[i][j-1]&&map[i][j-1]!=3) add(num,Get(i,j-1),1),add(Get(i,j-1),num,0);
            if(map[i][j+1]&&map[i][j+1]!=3) add(num,Get(i,j+1),1),add(Get(i,j+1),num,0);
            if(map[i+1][j]&&map[i+1][j]!=3) add(num,Get(i+1,j),1),add(Get(i+1,j),num,0);
        }
    Dinic();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值