二分图最大点独立集问题。
要求在棋盘上放最多互不攻击的骑士,即在棋盘中拿走最少的骑士,使得剩下的骑士互不攻击。
黄格只能攻击红格,红格也只能攻击黄格,所以考虑建立二分图。
源点向所有红格连流量为1的边,所有黄格向汇点连流量为一的边,再由红格向它能攻击到的黄格连流量为1的边,有障碍物的点不连边。
每找到一条增广路,即从棋盘中拿走一个骑士,所以最后的答案就是总点数-障碍数-最大匹配数。
#include<complex> #include<cstdio> using namespace std; const int INF=0x3f3f3f3f; const int N=4e4+7; const int dx[]={-1,-1,1,1,-2,-2,2,2},dy[]={-2,2,-2,2,-1,1,-1,1}; struct node{ int v,f,nxt; }e[N*10]; int n,m,Enum=1,s,t; int front[N],cur[N],deep[N]; int q[N]; bool vis[201][201]; int qread() { int x=0; char ch=getchar(); while(ch<'0' || ch>'9')ch=getchar(); while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} return x; } void Insert(int u,int v,int w) { e[++Enum].v=v;e[Enum].f=w;e[Enum].nxt=front[u];front[u]=Enum; e[++Enum].v=u;e[Enum].f=0;e[Enum].nxt=front[v];front[v]=Enum; } bool bfs() { for(int i=0;i<=t;i++) { deep[i]=0; cur[i]=front[i]; } int head=1,tail=0,u,v; deep[s]=1;q[++tail]=s; while(head<=tail) { u=q[head++]; for(int i=front[u];i;i=e[i].nxt) { v=e[i].v; if(!deep[v] && e[i].f) { deep[v]=deep[u]+1; if(v==t)return 1; q[++tail]=v; } } } return 0; } int dfs(int x,int cur_flow) { if(x==t)return cur_flow; int rest=cur_flow,v; for(int &i=cur[x];i;i=e[i].nxt) { v=e[i].v; if(deep[v]==deep[x]+1 && e[i].f && rest) { int new_flow=dfs(v,min(e[i].f,rest)); e[i].f-=new_flow; e[i^1].f+=new_flow; rest-=new_flow; if(!rest)return cur_flow; } } deep[x]=0; return cur_flow-rest; } int Dinic() { int res=0; while(bfs()) res+=dfs(s,INF); return res; } bool can(int x,int y) { if(x<1 || x>n || y<1 || y>n || vis[x][y])return 0; return 1; } int main() { scanf("%d%d",&n,&m); s=0;t=n*n+1; for(int i=1;i<=m;i++) vis[qread()][qread()]=1; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(!vis[i][j]) if((i+j)&1) Insert(s,(i-1)*n+j,1); else Insert((i-1)*n+j,t,1); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(!vis[i][j] && (i+j)&1) for(int k=0;k<8;k++) { int xx=i+dx[k],yy=j+dy[k]; if(can(xx,yy)) Insert((i-1)*n+j,(xx-1)*n+yy,1); } printf("%d ",n*n-m-Dinic()); return 0; }