Description
给定一个 N 行 N 列的棋盘,已知某些格子禁止放置。
求最多能往棋盘上放多少块的长度为 2 、宽度为 1 的骨牌,骨牌的边界与格线重合(骨牌占用两个格子),
并且任意两张骨牌都不重叠。
输入格式
第一行包含两个整数 N 和 t ,其中 t 为禁止放置的格子的数量。
接下来t行每行包含两个整数 x 和 y ,表示位于第 x 行第 y 列的格子禁止放置,行列数从 1 开始。
输出格式
输出一个整数,表示结果。
数据范围
1≤N≤100
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; }