[BZOJ]2150: 部落战争 二分图匹配

Description

lanzerb的部落在A国的上部,他们不满天寒地冻的环境,于是准备向A国的下部征战来获得更大的领土。 A国是一个M*N的矩阵,其中某些地方是城镇,某些地方是高山深涧无人居住。lanzerb把自己的部落分成若干支军队,他们约定: 1. 每支军队可以从任意一个城镇出发,并只能从上往向下征战,不能回头。途中只能经过城镇,不能经过高山深涧。 2. 如果某个城镇被某支军队到过,则其他军队不能再去那个城镇了。 3. 每支军队都可以在任意一个城镇停止征战。 4. 所有军队都很奇怪,他们走的方法有点像国际象棋中的马。不过马每次只能走1*2的路线,而他们只能走R*C的路线。 lanzerb的野心使得他的目标是统一全国,但是兵力的限制使得他们在配备人手时力不从心。假设他们每支军队都能顺利占领这支军队经过的所有城镇,请你帮lanzerb算算至少要多少支军队才能完成统一全国的大业。

题解:

这个有向无环图为什么可以用二分图匹配呢?FYC大神告诉我:比如说对于一条路径1->2->3->4,直接建二分图,那么最后1、2、3都会找到匹配,最大匹配数为3。实际上,对于每一条路径,只有结尾的点没有匹配,其他的点都能找到匹配,所以每一个没有匹配的点实际上代表了一条路径,所以最小路径覆盖=总点数-最大匹配。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=60;
int n,m,r,c,num[maxn][maxn],map[maxn][maxn],T=0;
char str[maxn];
int tx[10],ty[10];
bool in(int x,int y)
{
    if(x>0&&y>0&&x<=n&y<=m)return true;
    return false;
}
struct Edge{int y,next;}e[maxn*maxn*8];
int last[maxn*maxn],len=0,sum=0;
void ins(int x,int y)
{
    int t=++len;
    e[t].y=y;e[t].next=last[x];last[x]=t;
}
int chw[maxn*maxn],match[maxn*maxn];
bool findm(int x)
{
    for(int i=last[x];i;i=e[i].next)
    {
        int y=e[i].y;
        if(chw[y]!=T)
        {
            chw[y]=T;
            if(!match[y]||findm(match[y]))
            {
                match[y]=x;
                return true;
            }
        }
    }
    return false;
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&r,&c);
    tx[0]=tx[1]=r;tx[2]=tx[3]=c;
    ty[0]=-c;ty[1]=c;ty[2]=-r;ty[3]=r;
    for(int i=1;i<=n;i++)
    {
        scanf("%s",str+1);
        for(int j=1;j<=m;j++)
        map[i][j]=(str[j]=='.'?0:1);
    }
    int z=0,ans=0;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    num[i][j]=++z;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    if(!map[i][j])
    {
        sum++;
        for(int k=0;k<4;k++)
        {
            int x=i+tx[k],y=j+ty[k];
            if(in(x,y)&&!map[x][y])ins(num[i][j],num[x][y]);
        }
    }
    for(int i=1;i<=n*m;i++)
    {
        T++;
        if(findm(i))ans++;
    }
    printf("%d",sum-ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值