版权声明:https://blog.csdn.net/huashuimu2003 https://blog.csdn.net/huashuimu2003/article/details/88811857
title
CH 6801
描述
给定一个N行N列的棋盘,已知某些格子禁止放置。求最多能往棋盘上放多少块的长度为2、宽度为1的骨牌,骨牌的边界与格线重合(骨牌占用两个格子),并且任意两张骨牌都不重叠。N≤100。
输入格式
第一行为n,t(表示有t个删除的格子)
第二行到t+1行为x,y,分别表示删除格子所在的位置
x为第x行,y为第y列,行列编号从1开始。
输出格式
一个数,即最多能放的骨牌数
样例输入
8 0
样例输出
32
analysis
虽说《算法竞赛进阶指南》几乎没有裸题,但这题快可以算一道了。
这题我们设下标x,y的和是奇数的点为奇点,否则就是偶点。
如果相邻的两个点都可以放骨牌,那么我们就见一条从奇点出发(或从偶点)到另一个点的一个边。
然后这个棋盘就成了一个二分图,奇点和偶点为两个集合,我们选择尽量多的边,但两边之间没有公共点(因为一个点上不能放两块骨牌)。
根据二分图匹配……不用根据了,这就是一个求二分图最大匹配的问题。
code
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+10;
const int dic[4][2]={{-1,0},{0,-1},{0,1},{1,0}};
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
int ver[maxn<<1],Next[maxn<<1],head[maxn],len;
inline void add(int x,int y)
{
ver[++len]=y,Next[len]=head[x],head[x]=len;
}
int match[maxn];
bool vis[maxn];
inline bool dfs(int x)
{
for (int i=head[x]; i; i=Next[i])
{
int y=ver[i];
if (!vis[y])
{
vis[y]=1;
if (!match[y] || dfs(match[y]))
{
match[y]=x;
return true;
}
}
}
return false;
}
int n,t;
inline int getnum(int x,int y)
{
return (x-1)*n+y;
}
int del[maxn][maxn];
int main()
{
read(n);read(t);
for (int i=1; i<=t; ++i)
{
int x,y;
read(x);read(y);
del[x][y]=1;
}
for (int i=1; i<=n; ++i)
for (int j=1; j<=n; ++j)
if (!del[i][j])
for (int k=0; k<=3; ++k)
{
int x=i+dic[k][0],y=j+dic[k][1];
if (x>0 && y>0 && x<=n && y<=n && !del[x][y] && (x+y)%2)
add(getnum(i,j),getnum(x,y));
}
int ans=0;
for (int i=1; i<=getnum(n,n); ++i)
{
memset(vis,0,sizeof(vis));
if (dfs(i)) ++ans;
}
printf("%d\n",ans);
return 0;
}