给定一个N行N列的棋盘,已知某些格子禁止放置。
求最多能往棋盘上放多少块的长度为2、宽度为1的骨牌,骨牌的边界与格线重合(骨牌占用两个格子),并且任意两张骨牌都不重叠。
输入格式
第一行包含两个整数N和t,其中t为禁止放置的格子的数量。
接下来t行每行包含两个整数x和y,表示位于第x行第y列的格子禁止放置,行列数从1开始。
输出格式
输出一个整数,表示结果。
数据范围
1 ≤ N ≤ 100 1≤N≤100 1≤N≤100
输出样例:
8 0
输出样例:
32
分析:
为什么可以用匈牙利算法?
将 方 阵 从 左 到 右 , 从 上 到 下 依 次 染 色 : 将方阵从左到右,从上到下依次染色: 将方阵从左到右,从上到下依次染色:
我 们 发 现 , 每 个 点 与 其 相 邻 点 都 是 相 异 的 。 我们发现,每个点与其相邻点都是相异的。 我们发现,每个点与其相邻点都是相异的。
所 以 , 若 在 某 个 1 × 2 的 方 格 中 放 一 块 骨 牌 , 就 在 这 两 个 方 格 点 之 间 连 接 一 条 边 。 所以,若在某个1×2的方格中放一块骨牌,就在这两个方格点之间连接一条边。 所以,若在某个1×2的方格中放一块骨牌,就在这两个方格点之间连接一条边。
我 们 的 目 标 是 : 在 指 定 的 部 分 方 格 内 , 放 尽 量 多 的 骨 牌 , 我们的目标是:在指定的部分方格内,放尽量多的骨牌, 我们的目标是:在指定的部分方格内,放尽量多的骨牌,
即 : 在 给 定 的 部 分 点 中 , 连 接 尽 量 多 的 边 。 即:在给定的部分点中,连接尽量多的边。 即:在给定的部分点中,连接尽量多的边。
我 们 发 现 , 这 是 一 个 二 分 图 的 最 大 匹 配 问 题 。 我们发现,这是一个二分图的最大匹配问题。 我们发现,这是一个二分图的最大匹配问题。
注意:
在 二 分 图 问 题 中 , 通 常 我 们 讨 论 的 是 无 向 边 。 在二分图问题中,通常我们讨论的是无向边。 在二分图问题中,通常我们讨论的是无向边。
但 是 , 由 于 点 分 成 了 两 类 , 我 们 每 次 考 虑 一 类 的 点 与 另 一 类 的 点 匹 配 , 但是,由于点分成了两类,我们每次考虑一类的点与另一类的点匹配, 但是,由于点分成了两类,我们每次考虑一类的点与另一类的点匹配,
故 在 建 图 的 过 程 中 , 仅 需 连 接 一 个 方 向 。 故在建图的过程中,仅需连接一个方向。 故在建图的过程中,仅需连接一个方向。
点 的 分 类 : 将 坐 标 和 为 奇 数 的 点 分 为 一 类 , 坐 标 和 为 偶 数 的 点 分 为 一 类 。 点的分类:将坐标和为奇数的点分为一类,坐标和为偶数的点分为一类。 点的分类:将坐标和为奇数的点分为一类,坐标和为偶数的点分为一类。
代码:
#include
#include
#include
#define P pair
#define x first
#define y second
using namespace std;
const int N=110;
int n,m;
int g[N][N],st[N][N];
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
P match[N][N];
bool Find(int x,int y)
{
for(int i=0;i<4;i++)
{
int a=x+dir[i][0],b=y+dir[i][1];
if(a<1||a>n||b<1||b>n) continue;
if(g[a][b] || st[a][b]) continue;
st[a][b]=true;
P t=match[a][b];
if(t.x==-1 || Find(t.x,t.y))
{
match[a][b]={x,y};
return true;
}
}
return false;
}
int main()
{
cin>>n>>m;
while(m--)
{
int a,b;
cin>>a>>b;
g[a][b]=true;
}
memset(match,-1,sizeof match);
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,false,sizeof st); //搜过的点下次可以再搜,改变它的匹配
if(Find(i,j)) res++;
}
cout<<res<<endl;
return 0;
}