二分图最大匹配(棋盘覆盖)

棋盘覆盖

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

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

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

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

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

数据范围
1≤N≤100,
0≤t≤100

输入样例:
8 0
输出样例:
32

题目分析:这题乍一看是状压DP,但是题目数据范围是100比较大,所有==所以要考虑别的思路,由于卡片只能放到相邻的两个格子当中,我们把每个格子看作一个点,相邻两个格子连出一条边,于是这个题就抽象成了最多选多少条边,所有选出的边之间没有公共点,这就是最大匹配问题。
就比如下面这个图,黑色区域是禁止放置的
请添加图片描述
经过匹配之后:
请添加图片描述

求最大匹配问题可以用匈牙利算法求解,但是用匈牙利算法前提需要图是二分图,所以我们需要判断一下是不是二分图。
一个n*n的矩阵,我们通过染色把黑色区域看作一个集合,白色区域看作一个集合。
两个集合当中每个点相邻的点的颜色都是不同的,通过染色法判定我们发现这就是一个二分图。而且黑色区域每个点坐标和为偶数,白色区域每个点坐标和为奇数,因此我们就可以用匈牙利算法进行求解最大匹配问题。

请添加图片描述
实现步骤:
1.建图(标记禁止放置的点)
2.对二分图中的黑色区域集合进行匹配并且更新答案
3.输出最大匹配数

具体ACcode:

#include<bits/stdc++.h>
#define x first
#define y second

using namespace std;

typedef pair<int ,int> PII;
typedef long long ll;

const int N = 110;

int n,m;
PII match[N][N];//标记匹配点
int g[N][N];//存棋盘上坏的点
int st[N][N];//标记是否匹配过

int dx[4]={
    
    -1,0,1,0},dy[4]={
    
    0,1,0,-1};//四个方向匹配

bool find(int x,int y)
{
    
    
    for(int i=0;i<4;i++)
    {
    
    
        int nx=x+dx[i],ny=y+dy[i];
        if(nx<1||nx>n||ny<1||ny>n)continue;//不能超出棋盘
        if(st[nx][ny]||g[nx][ny])continue;//不能匹配过或者是禁止放置的点

        st[nx][ny]=1;//标记匹配过
        PII t=match[nx][ny];
        if(t.x==0||find(t.x,t.y))
        //如果未被标记或者前一个匹配点还可以找到另外一个人匹配
        {
    
    
            match[nx][ny]={
    
    x,y};//更新匹配点
            return true;
        }
    }
    return false;
}

int main()
{
    
    
    cin>>n>>m;
    while(m--)
    {
    
    
        int x,y;
        cin>>x>>y;
        g[x][y]=1;//标记禁止放置的点
    }
    int res=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
    
    
            if((i+j)%2&&!g[i][j])//对二分图中的黑色区域集合进行匹配
            {
    
    
                memset(st,0,sizeof st);
                if(find(i,j))res++;
            }
        }
    
    cout<<res<<endl;

    return 0;
}

猜你喜欢

转载自blog.csdn.net/Stephen_Curry___/article/details/126389059