[bzoj 4808]马

124 篇文章 2 订阅
8 篇文章 0 订阅

众所周知,马后炮是中国象棋中很厉害的一招必杀技。”马走日字”。本来,如果在要去的方向有别的棋子挡住(俗称”蹩马腿”),则不允许走过去。为了简化问题,我们不考虑这一点。马跟马显然不能在一起打起来,于是rly在一天再次借来了许多许多的马在棋盘上摆了起来……但这次,他实在没兴趣算方案数了,所以他只想知道在N×M的矩形方格中摆马使其互不吃到的情况下的最多个数。但是,有一个很不幸的消息,rly由于玩得太Happy,质量本来就不好的棋盘被rly弄坏了,不过幸好只是破了其中的一些格子(即不能再放子了),问题还是可以继续解决的。

这道题同bzoj 3175一样,但问题是如果是普通的二分图匹配的话,这里就不会像bzoj 3175那样A,会T。要加什么黑白染色,但本蒟蒻不会啊。然后神奇地发现了改了一下dx,dy数组内数字的位置,就会快很多。太玄学了。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;
struct node
{
    int x,y,next;
}a[400010];int len,last[40010];
void ins(int x,int y)
{
    len++;
    a[len].x=x;a[len].y=y;
    a[len].next=last[x];last[x]=len;
}
int dx[8]={-1,-2, 1, 2,-1,-2,1,2};
int dy[8]={-2,-1,-2,-1, 2, 1,2,1};
int chw[40010],sd;
int s[210][210],match[40010];
int sp[210][210];
bool find_muniu(int x)
{
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(chw[y]!=sd)
        {
            chw[y]=sd;
            if(match[y]==0 || find_muniu(match[y])==true)
            {
                match[y]=x;
                return true;
            }
        }
    }
    return false;
}
int main()
{
    int n,m,ss=0;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            scanf("%d",&s[i][j]);
            if(s[i][j]==0)sp[i][j]=++ss;
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(s[i][j]==0)
            {   
                for(int k=0;k<8;k++)
                {
                    int xx=i+dx[k],yy=j+dy[k];
                    if(xx>=1 && xx<=n && yy>=1 && yy<=m && s[xx][yy]==0)ins(sp[i][j],sp[xx][yy]);
                }
            }
        }
    }
    int ans=0;
    for(int i=1;i<=ss;i++)
    {
        sd++;
        if(find_muniu(i)==true)ans++;
    }
    printf("%d\n",ss-ans/2);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值