[题目链接]https://www.luogu.com.cn/problem/P3355
【题目描述】
题目描述
在一个 n*n个方格的国际象棋棋盘上,马(骑士)可以攻击的棋盘方格如图所示。棋盘上某些方格设置了障碍,骑士不得进入
对于给定的 n*n 个方格的国际象棋棋盘和障碍标志,计算棋盘上最多可以放置多少个骑士,使得它们彼此互不攻击
输入格式
第一行有 2 个正整数n 和 m (1<=n<=200, 0<=m<
n
2
n^2
n2),分别表示棋盘的大小和障碍数。接下来的 m 行给出障碍的位置。每行 2 个正整数,表示障碍的方格坐标。
输出格式
将计算出的共存骑士数输出
【分析】
正解是网络流,但是我硬是用匈牙利卡常过了
分析棋盘,连接棋盘的“日”字格对角线,表示两个端点不能共存。原题转化为去掉最少的点数使得任意两点之间都没有边相连,求一个最大独立集。
有定理二分图的最大独立集 = 点数-最大匹配数。
且可以看出骑士所影响的点和它所在的点颜色一定不同。对棋盘进行黑白染色,行号加列号为偶数的格子染成白色,行号加列号为奇数的格子染成黑色,黑、白色的格子分别作为二分图的左、右部结点。
用 (i^j)&1的方法快速染色,这也是二分图和网络流常用的技巧
如果两个格子是“日”的对角,能够相互攻击到,则在对应的结点之间连边(因为只有八个方向,所以不用额外建图,for循环判断)
匈牙利算法求二分图的最大独立集
卡常优化,267ms
#include <bits/stdc++.h>
using namespace std;
const int maxn = 205;
int n,m,ans;
bool mp[maxn][maxn],vis[maxn][maxn];
const int dx[8]={1,1,-1,-1,2,2,-2,-2};
const int dy[8]={-2,2,-2,2,-1,1,-1,1};
inline int get()
{
char c;int f=0,d=1;
while((c=getchar())<48||c>57)if(c=='-')d=-1;f=(f<<3)+(f<<1)+c-48;
while((c=getchar())>=48&&c<=57)f=(f<<3)+(f<<1)+c-48;
return d*f;
}
inline void read()
{ //int x,y;
// scanf("%d%d",&n,&m);
n = get();m = get();
for(int i=1;i<=m;i++)
{
//scanf("%d%d",&x,&y);
mp[get()][get()] = 1;
}
}
int match[maxn][maxn][2];
inline int find(int x,int y)
{
int xx,yy;
for(int i=0;i<8;i++)
{
xx = x+dx[i],yy = y+dy[i];
if(xx<1 || yy <1 || xx>n || yy>n) continue;
if(vis[xx][yy] || mp[xx][yy]) continue;
vis[xx][yy] = 1;
if(!match[xx][yy][0] || find(match[xx][yy][0],match[xx][yy][1]))
{
match[xx][yy][0] = x;
match[xx][yy][1] = y;
return 1;
}
}
return 0;
}
inline void work()
{ ans = 0;
for(register int i=1;i<=n;i++)
{
for(register int j=1;j<=n;j++)
{
if(!((i^j)&1) || mp[i][j]) continue;//快速染色
memset(vis,0,sizeof(vis));
ans+=find(i,j);
}
}
printf("%d",n*n-m-ans);
}
int main()
{ //freopen("test.in","r",stdin);
//freopen("test.out","w",stdout);
read();
work();
return 0;
}