题意:在n*m的网格上填马,其攻击范围是±3,±1这种类型,每个格子有个权值,有些格子可以选,有些不能选,求一种字典序最小的,马互不攻击的,权值之和最大的一种方案
明显按行奇偶染色,就变成了二分图上的最大权独立集的问题,这个是个经典模型,然后考虑怎么输方案,按字典序枚举每个位置,如果想让这个位置必须选,那么就是它连向源或汇的边变为oo,使得最小割割不开,则判断其合法性就是看有没有一条到汇(源)点的增广路,也就是一次bfs就可以了,因此判断复杂度也是o((n*m)^2)的。比赛的时候还是黄哥机智的提出了这个idea,否则我就得每次暴力流一遍了。
其实这题烦的不是算法,而是各种情况,譬如至少放一个马,或者是有负数,同时你又必须选它之类的,比赛时写了一遍,之后又re了一遍,改到拍不出错之后,又手构了几种情况才终于过了。
#include <cstdio> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> const int dx[8]={-1,-3,-3,-1,1,3,3,1}; const int dy[8]={-3,-1,1,3,3,1,-1,-3}; const int oo=1073741819; using namespace std; int tail[20000],next[500000],sora[500000]; int flow[1050][1050],col[1050][1050],id[1050][1050],g[2][1050],d[1050],st[1050],w[1050][1050]; int s,t,n,m,ss,tot; void origin() { s=n*m+1,t=s+1,ss=t; for (int i=1;i<=t;i++) tail[i]=i,next[i]=0; } void link(int x,int y,int z) { ++ss,next[tail[x]]=ss,tail[x]=ss,sora[ss]=y,next[ss]=0; ++ss,next[tail[y]]=ss,tail[y]=ss,sora[ss]=x,next[ss]=0; flow[x][y]=z,flow[y][x]=0; } void doit(int x,int y) { for (int i=0;i<=7;i++) { int nx=x+dx[i],ny=y+dy[i]; if (nx<1 || nx>n || ny<1 || ny>m || col[nx][ny]==2) continue; link(id[x][y],id[nx][ny],oo); } } bool bfs(int s,int t) { int h,r,ne,na; for (int i=1;i<=t;i++) d[i]=oo; h=r=0; st[r=1]=s,d[s]=0; for (;h<r;) { ne=st[++h]; for (int i=ne;next[i];) { i=next[i],na=sora[i]; if (flow[ne][na] && d[ne]+1<d[na]) { d[na]=d[ne]+1; st[++r]=na; } } } return d[t]<oo; } int dfs(int x,int low) { if (x==t) return low; int sum=0,ne,tmp; for (int i=x;next[i];) { i=next[i],ne=sora[i]; if (flow[x][ne] && d[x]+1==d[ne]) { if (flow[x][ne]<low) tmp=dfs(ne,flow[x][ne]); else tmp=dfs(ne,low); if (!tmp) d[ne]=oo; flow[x][ne]-=tmp,flow[ne][x]+=tmp,sum+=tmp,low-=tmp; if (!low) break; } } return sum; } void bfs2(int s) { int h,r,ne,na; for (int i=1;i<=t;i++) d[i]=oo; h=r=0; st[r=1]=s,d[s]=0; for (;h<r;) { ne=st[++h]; for (int i=ne;next[i];) { i=next[i],na=sora[i]; if (flow[na][ne] && d[ne]+1<d[na]) { d[na]=d[ne]+1; st[++r]=na; } } } } int T; int main() { freopen("input.txt","r",stdin); freopen("output.txt","w",stdout); scanf("%d",&T); for (int test=1;T;T--,test++) { printf("Case %d: ",test); scanf("%d%d",&n,&m); origin(); for (int i=1,s1=1;i<=n;i++) for (int j=1;j<=m;j++,s1++) scanf("%d",&w[i][j]),col[i][j]=0,id[i][j]=s1; int p,q; scanf("%d",&p); for (int i=1;i<=p;i++) { int x,y; scanf("%d%d",&x,&y); x++,y++; col[x][y]=1; } scanf("%d",&q); for (int i=1;i<=q;i++) { int x,y; scanf("%d%d",&x,&y); x++,y++; col[x][y]=2; } int M,M1,M2; tot=0,M=-oo,M1=0,M2=0; bool flag=0; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if (col[i][j]!=2) { if (col[i][j]==1 || w[i][j]>0) tot+=w[i][j],flag=1; if (w[i][j]>M) M=w[i][j],M1=i,M2=j; } if (!flag && M<0) { printf("%d\n",M); printf("%d %d\n",M1-1,M2-1); continue; } for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if (col[i][j]!=2) { if (i&1) { if (col[i][j]==1) link(s,id[i][j],oo),doit(i,j); else if (w[i][j]>=0) link(s,id[i][j],w[i][j]),doit(i,j);; } else { if (col[i][j]==1) link(id[i][j],t,oo); else if (w[i][j]>=0) link(id[i][j],t,w[i][j]); } } int sum=0; for (;bfs(s,t);sum+=dfs(s,oo)) ; printf("%d\n",tot-sum); for (int i=1;i<=s-1;i++) g[0][i]=d[i]; bfs2(t); for (int i=1;i<=s-1;i++) g[1][i]=d[i]; int k=0,fl=0,lf=0; //cout<<flow[id[5][2]][id[2][1]]<<' '<<flow[s][id[5][2]]<<endl; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if (col[i][j]!=2 && (k!=tot-sum || fl!=p || !lf)) { if (col[i][j]==1) { printf("%d %d\n",i-1,j-1); k+=w[i][j],fl++,lf++; continue; } if (w[i][j]<0) continue; //cout<<i<<'-'<<j<<endl; if (i&1) { if (g[1][id[i][j]]<oo) continue; else { printf("%d %d\n",i-1,j-1); k+=w[i][j],lf++; flow[s][id[i][j]]=oo; bfs(s,t); for (int i=1;i<=s-1;i++) g[0][i]=d[i]; } } else { if (g[0][id[i][j]]<oo) continue; else { printf("%d %d\n",i-1,j-1); k+=w[i][j],lf++; flow[id[i][j]][t]=oo; bfs2(t); for (int i=1;i<=s-1;i++) g[1][i]=d[i]; } } } } return 0; }