372. 棋盘覆盖
题目链接
372. 棋盘覆盖
给定一个 \(N\) 行 \(N\) 列的棋盘,已知某些格子禁止放置。
求最多能往棋盘上放多少块的长度为 \(2\)、宽度为 \(1\) 的骨牌,骨牌的边界与格线重合(骨牌占用两个格子),并且任意两张骨牌都不重叠。
输入格式
第一行包含两个整数 \(N\) 和 \(t\),其中 \(t\) 为禁止放置的格子的数量。
接下来 \(t\) 行每行包含两个整数 \(x\) 和 \(y\),表示位于第 \(x\) 行第 \(y\) 列的格子禁止放置,行列数从 \(1\) 开始。
输出格式
输出一个整数,表示结果。
数据范围
\(1 \le N \le 100\),
\(0 \le t \le 100\)
输入样例:
8 0
输出样例:
32
解题思路
匈牙利算法
这样的图很容易构造成一个二分图,即对于所有 \((i,j)\) 的格子,如果 \((i+j)\%2=0\) 的话则染为白色,否则染为黑色
除去障碍物,任意两个连续的方块之间连边,本质上就是要求出点不重复的最多边数,即二分图的最大匹配,用匈牙利算法求解即可
- 时间复杂度:\(O(n^4)\)
代码
// Problem: 棋盘覆盖
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/374/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=105;
int n,t;
bool g[N][N],st[N][N];
int dx[]={-1,0,1,0},dy[]={0,1,0,-1};
PII match[N][N];
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||g[nx][ny])continue;
if(!st[nx][ny])
{
st[nx][ny]=true;
if(match[nx][ny].fi==0&&match[nx][ny].se==0||find(nx,ny))
{
match[nx][ny]={x,y};
return true;
}
}
}
return false;
}
int main()
{
scanf("%d%d",&n,&t);
for(int i=1;i<=t;i++)
{
int x,y;
scanf("%d%d",&x,&y);
g[x][y]=true;
}
int res=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if((i+j)%2==0||g[i][j])continue;
memset(st,0,sizeof st);
if(find(i,j))res++;
}
printf("%d",res);
return 0;
}