这道题提醒我,要有将棋盘黑白染色的意识,尤其是看到相邻格子这样的条件的时候,然后就是要用到与其有关的性质与特点以体现其作用,这道题就是用到了黑格子与白格子之间的关系进行的,其出发点是每次一定会给一个黑格子与一个白格子均加一,那么最后黑白格子所加量相同(最关键的地方)。
然后呢,还要观察,最终高度与行动次数一一对应,于是求解他们两个是等效的,然后发现如果最后高度确定,是很好验证是否可行的,就是方格下水道。进一步分析,当黑格与白格的数量不同那么最终高度一定,可以一下判解。当数量相同的时候关于最终高度是单调的,因为h可以,h+1也可以,所以这道题就这么解决了。
为什么我想不到啊(苣蒻++)。
#include <cstdio> #include <cstring> #include <algorithm> #define pos(a,b) (((a)-1)*m+(b)) typedef long long LL; const int N=42; const int P=N*N; const int E=P*10; const LL Inf=0x3f3f3f3f3f3f3f3fLL; struct V{ int to,next; LL f; }c[E]; int head[P],t; inline void add(int x,int y,LL z){ c[++t].to=y,c[t].next=head[x],head[x]=t,c[t].f=z; } inline void clear(){ memset(head,0,sizeof(head)),t=1; } int n,m; int S,T; int deep[P],q[P],front,back; inline bool bfs(){ memset(deep,0,sizeof(deep)); front=back=0,q[back++]=S,deep[S]=1; while(front!=back){ int x=q[front++]; for(int i=head[x];i;i=c[i].next) if(c[i].f&&deep[c[i].to]==0){ deep[c[i].to]=deep[x]+1; if(c[i].to==T)return true; q[back++]=c[i].to; } }return false; } inline LL dfs(int x,LL v){ if(x==T||v==0)return v; LL ret=0; for(int i=head[x];i;i=c[i].next) if(c[i].f&&deep[c[i].to]==deep[x]+1){ LL f=dfs(c[i].to,std::min(c[i].f,v)); ret+=f,v-=f,c[i].f-=f,c[i^1].f+=f; if(v==0)break; } if(ret==0)deep[x]=0; return ret; } int cnt[2]; LL sum[2]; int max,val[N][N]; inline LL dinic(){ LL ret=0; while(bfs())ret+=dfs(S,Inf); return ret; } inline bool check(LL ans){ clear();LL ret=0; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) if((i+j)&1){ if(i>1) add(pos(i,j),pos(i-1,j),Inf),add(pos(i-1,j),pos(i,j),0); if(j>1) add(pos(i,j),pos(i,j-1),Inf),add(pos(i,j-1),pos(i,j),0); if(i<n) add(pos(i,j),pos(i+1,j),Inf),add(pos(i+1,j),pos(i,j),0); if(j<m) add(pos(i,j),pos(i,j+1),Inf),add(pos(i,j+1),pos(i,j),0); add(S,pos(i,j),ans-val[i][j]),add(pos(i,j),S,0); ret+=ans-val[i][j]; }else add(pos(i,j),T,ans-val[i][j]),add(T,pos(i,j),0); return ret==dinic(); } inline void work1(){ if((sum[0]-sum[1])%(cnt[0]-cnt[1])!=0){ puts("-1");return; } LL ans=(sum[0]-sum[1])/(cnt[0]-cnt[1]); if(check(ans)) printf("%lld ",(ans*n*m-(sum[0]+sum[1]))>>1LL); else puts("-1"); } inline void work2(){ if(sum[0]!=sum[1]){ puts("-1");return; } LL l=max,r=Inf/5000LL,mid,ans=0; while(l<=r){ mid=(l+r)>>1; if(check(mid)) ans=mid,r=mid-1; else l=mid+1; } if(ans==0)puts("-1"); else printf("%lld ",(ans*n*m-(sum[0]+sum[1]))>>1LL); } int main(){ int test;scanf("%d",&test); while(test--){ scanf("%d%d",&n,&m); memset(cnt,0,sizeof(cnt)); memset(sum,0,sizeof(sum)); max=0,S=n*m+1,T=n*m+2; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j){ scanf("%d",&val[i][j]); ++cnt[(i+j)&1],sum[(i+j)&1]+=val[i][j]; max=std::max(max,val[i][j]); } if(cnt[0]!=cnt[1])work1(); else work2(); }return 0; }