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

高一的时候似乎做了……然后我现在发现不会做了。。
(高一代码抄多了QAQ)

题目要求的是将狼和羊分开,可以看做是分成两个点集,要使代价最小,那么也就是求一个最小割。
首先S->狼连边,权值为+∞。羊->T连边,权值为+∞,这些边都是不可割的。
然后我们把相邻的狼->羊连边,容量为1,代表在这个狼和羊之间建立篱笆的代价为1。
然后关于空地的问题。我们把狼->空地连边,容量为1,空地->羊连边,容量也为1。空地之间要连双向边,因为空地既可以属于S集,也可以属于T集。建完图就可以跑最大流了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define INF 100000007
using namespace std;
const int dx[5]={0,1,0,-1,0};
const int dy[5]={0,0,1,0,-1};
int head[10005],q[10005],dis[10005],list[500001],next[500001],key[500001],a[105][105];
int n,m,ans,xx,yy,cnt=1,T=10001;
void insert(int x,int y,int z)
{
    next[++cnt]=head[x];
    head[x]=cnt;
    list[cnt]=y;
    key[cnt]=z;
}
bool BFS()
{
    int t=0,w=1,i,now;
    memset(dis,-1,sizeof(dis));
    q[0]=0; dis[0]=0;
    while (t<w)
    {
        now=q[t]; t++; i=head[now];
        while (i)
        {
            if (key[i]&&dis[list[i]]==-1)
            {
                dis[list[i]]=dis[now]+1;
                q[w++]=list[i];
            }
            i=next[i];
        }
    }
    return dis[T]==-1?0:1;
}
int dfs(int x,int f)
{
    if (x==T) return f;
    int w,used=0,i;
    i=head[x];
    while (i)
    {
        if (key[i]&&dis[list[i]]==dis[x]+1)
        {
            w=f-used;
            w=dfs(list[i],min(w,key[i]));
            key[i]-=w;
            key[i^1]+=w;
            used+=w;
            if (used==f) return f;
        }
        i=next[i];
    }
    if (!used) dis[x]=-1;
    return used;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            scanf("%d",&a[i][j]);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
        {
            if (a[i][j]==1) { insert(0,(i-1)*m+j,INF); insert((i-1)*m+j,0,0); }
            if (a[i][j]==2) { insert((i-1)*m+j,T,INF); insert(T,(i-1)*m+j,0); }
            for (int k=1;k<=4;k++)
            {
                xx=i+dx[k];
                yy=j+dy[k];
                if (xx<1||xx>n||yy<1||yy>m||a[i][j]==2) continue;
                if (a[i][j]!=1||a[xx][yy]!=1)
                {
                    insert((i-1)*m+j,(xx-1)*m+yy,1);
                    insert((xx-1)*m+yy,(i-1)*m+j,0);
                }
            }
        }
    while (BFS()) ans+=dfs(0,INF);
    printf("%d",ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值