题目大意:
思路:
这题是真的烦。。。
n<=200的数据很容易让我们想到匈牙利算法,所以就打了一发匈牙利。
然后T了。。。
于是就开始优化。
优化了我2个小时。。。
正题:
匈牙利算法(二分图)
不难发现,上图黄色格子不能攻击到其他黄色黄色格子,红色格子不能攻击到其他红色格子。
那么就可以把图以奇偶拆分,形成二分图。
然后枚举每个点一级他可以攻击到的点(增广路)。
最后用总格子数最大匹配最多能放的骑士个数。
代码:
#include<cstdio>
#include<cstring>
using namespace std;
const short dx[8]={1,1,-1,-1,2,2,-2,-2};
const short dy[8]={-2,2,-2,2,-1,1,-1,1}; //八个方向
int n,m,sum,eve,odd,g[202][202],link[20002],a[20002][3],xx,yy,f;
bool vi[20002],ok[202][202];
char ch;
int read() //输入流
{
f=0;
while(ch=getchar(),ch<=47||ch>=58);f=(f<<3)+(f<<1)+ch-48;
while(ch=getchar(),ch>=48&&ch<=57) f=(f<<3)+(f<<1)+ch-48;
return f;
}
bool find(int x) //匈牙利
{
int d=0,p=0;
for (int i=0;i<8;i++)
{
xx=a[x][1]+dx[i];
yy=a[x][2]+dy[i];
if(xx<1||xx>n||yy<1||yy>n) continue; //出界
if(ok[xx][yy]||vi[g[xx][yy]]) continue; //已经走过
d=g[xx][yy]; //记录
p=link[d];
link[d]=x;
vi[d]=true;
if(find(p)||!p) return true; //继续找增广路
link[d]=p;
}
return false;
}
int main()
{
n=read();
m=read();
for (int i=1;i<=m;i++)
ok[read()][read()]=true;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if(!ok[i][j])
{
if(!((i+j)&1))
g[i][j]=++odd; //奇数
else
{
g[i][j]=++eve; //偶数
a[eve][1]=i;
a[eve][2]=j;
}
}
sum=n*n-m;
for (int i=1;i<=eve;i++) //枚举偶数点
{
memset(vi,0,sizeof(vi));
if(find(i)) sum--; //有一种方法就减1
}
printf("%d\n",sum);
return 0;
}