题目大意:
思路:
这题是真的烦。。。
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; }