【codevs1922】骑士共存问题——网络流
给棋盘黑白染色,源点向不为障碍的奇点连一条权值为1的边,向可以攻击到的偶点连一条边,权值为inf;偶点向汇点(t=n*n+1)连一条权值为1的边。
跑最小割,最小割的意义就是看至少要放弃几个点(即这里不放骑士)才能使他们不会互相攻击,最后用总格数减去最小割时记得也要减去障碍数,即n*n-ans-m.
具体细节看代码:
#include<cstdio> #include<cstring> #include<iostream> const int inf=0x3f3f3f3f; using namespace std; int n,m,s,t; struct point { int flow,to,next; }e[500500]; int q[40050],d[40050],tot=1,first[40010],cur[40010]; bool mapp[204][204]; int cc[10][2]={{1,2},{2,1},{-1,2},{-1,-2},{1,-2},{2,-1},{-2,1},{-2,-1}}; void insert(int u,int v,int w) { tot++;e[tot].to=v;e[tot].flow=w;e[tot].next=first[u];first[u]=tot; tot++;e[tot].to=u;e[tot].flow=0;e[tot].next=first[v];first[v]=tot; } bool bfs() { memset(d,-1,sizeof(d)); d[0]=0;q[0]=0; int head=0,tail=1; while(head!=tail) { int x=q[head++];if(head>=40000)head=0; for(int i=first[x];i;i=e[i].next) { if(d[e[i].to]==-1&&e[i].flow>0) { d[e[i].to]=d[x]+1; q[tail++]=e[i].to; if(tail>=40000)tail=0; } } } if(d[t]==-1)return false; return true; } int dfs(int x,int a) { if(x==t||a==0)return a; int flow=0,f; for(int& i=cur[x];i;i=e[i].next) { if(d[e[i].to]==d[x]+1&&(f=dfs(e[i].to,min(a,e[i].flow)))>0) { e[i].flow-=f; e[i^1].flow+=f; a-=f; flow+=f; if(!a)break; } } return flow; } int sum(int x,int y) { return (x-1)*n+y; } int main() { int x,y,ans=0; scanf("%d %d",&n,&m); s=0;t=n*n+1; for(int i=1;i<=m;i++) { scanf("%d %d",&x,&y); mapp[x][y]=1; } for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(!mapp[i][j]&&!((i+j)%2)) { insert(s,sum(i,j),1); for(int k=0;k<8;k++) { int xx=cc[k][0],yy=cc[k][1]; if(i+xx>0&&i+xx<=n&&j+yy>0&&j+yy<=n&&!mapp[i+xx][j+yy])insert(sum(i,j),sum(i+xx,j+yy),inf); } } else if(!mapp[i][j])insert(sum(i,j),t,1); } } while(bfs()) { for(int i=0;i<=t;i++)cur[i]=first[i]; ans+=dfs(0,inf); } printf("%d\n",n*n-ans-m); return 0; }