【二分图最大独立集】BZOJ4808[马]题解

题目概述

给出 n×m 的棋盘(有些位置有损坏),问最多能在其中放多少互不吃到的马(不能放在损坏位置中)。

解题报告

NOIP2017前的最后一题QAQ。

首先将棋盘 01 间隔染色,然后就成了二分图。

由于要放最多的马,其实就是最大独立集。

最大独立集 = 点数 最小点覆盖 = 点数 最大匹配。

示例程序

#include
using namespace std;
const int maxn=200,maxm=200,maxt=20000;
const int fl[8][2]={{-2,-1},{-2,1},{-1,-2},{-1,2},{1,-2},{1,2},{2,-1},{2,1}};

int n,m,tot[2],ID[2][maxn+5][maxm+5];
int E,lnk[maxt+5],nxt[maxt*8+5],son[maxt*8+5],who[maxt+5];
int ti,vis[maxt+5],ans;

#define check(x,y) (1<=(x)&&(x)<=n&&1<=(y)&&(y)<=m&&ID[0][x][y])
#define Add(x,y) son[++E]=(y),nxt[E]=lnk[x],lnk[x]=E
inline bool Find(int x)
{
    if (vis[x]==ti) return false;vis[x]=ti;
    for (int j=lnk[x];j;j=nxt[j])
        if (!who[son[j]]||Find(who[son[j]])) return who[son[j]]=x,true;
    return false;
}
int main()
{
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
    for (int j=1;j<=m;j++)
    {
        int x;scanf("%d",&x);if (x) continue;
        ID[i+j&1][i][j]=++tot[i+j&1];
    }
    for (int i=1;i<=n;i++)
    for (int j=1;j<=m;j++) if (i+j&1)
    for (int f=0;f<8;f++) if (check(i+fl[f][0],j+fl[f][1]))
        Add(ID[1][i][j],ID[0][i+fl[f][0]][j+fl[f][1]]);
    for (int i=1;i<=tot[1];i++) ti++,ans+=Find(i);
    return printf("%d\n",tot[0]+tot[1]-ans),0;
}

你可能感兴趣的:(BZOJ题解,二分图最大匹配)