棋盘覆盖(二分图匹配)

棋盘覆盖(ACwing)

Description

给定一个 N 行 N 列的棋盘,已知某些格子禁止放置。

求最多能往棋盘上放多少块的长度为 2 、宽度为 1 的骨牌,骨牌的边界与格线重合(骨牌占用两个格子),

并且任意两张骨牌都不重叠。

输入格式

第一行包含两个整数 N 和 t ,其中 t 为禁止放置的格子的数量。

接下来t行每行包含两个整数 x 和 y ,表示位于第 x 行第 y 列的格子禁止放置,行列数从 1 开始。

输出格式

输出一个整数,表示结果。

数据范围

1N100

Solution

此题乍看与二分图无关,但“不重叠”,“长度为 2 、宽度为 1”等字眼提示了我们

“不重叠” 与 “二分图中每个节点只能与 1 条边相连” 的条件契合

“长度为 2 、宽度为 1” 表示可在两个节点间连边,

“最多能放多少骨牌” 提示把骨牌作为边连接两个点,求最大匹配

但二分图还有一个条件:节点能分成独立的两个集合,每个集合内部有 0 条边

观察发现每个点只能与上下左右的点连边,于是按(行数 + 列数)的奇偶性将棋盘染成黑白相间,

按照颜色分成两个集合

Code

#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
const int N=1e4+10,M=1e5+10;
int head[N],nxt[M],to[M],ans,tot,n,m,a,b,match[N];
void add(int x,int y)
{
    to[++tot]=y,nxt[tot]=head[x],head[x]=tot;
}
bool flag[101][101],vis[N];
bool dfs(int x)
{
    for(int i=head[x],y;i;i=nxt[i])
        if(!vis[y=to[i]])
        {
            vis[y]=true;
            if(!match[y] || dfs(match[y]))
            {
                match[y]=x,match[x]=y;
                return true;
            }
        }
    return false;
}
int main()
{
    scanf("%d%d",&n,&m);
    while(m--)
    {
        scanf("%d%d",&a,&b);
        flag[a][b]=true;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            if(flag[i][j]) continue;
            int pos=(i-1)*n+j;
            if(!flag[i][j-1] && j>=2) add(pos,pos-1);
            if(!flag[i][j+1] && j<n) add(pos,pos+1);
            if(!flag[i-1][j] && i>1) add(pos,pos-n);
            if(!flag[i+1][j] && i<n) add(pos,pos+n);
        }
    for(int i=1;i<=n*n;i++)
        if(!match[i])
        {
               if(dfs(i)==1) ans++;
            memset(vis,0,sizeof(vis));
        }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/hsez-cyx/p/12348532.html