Exca王者之剑 bzoj-1324
题目大意:题目链接。
注释:略。
想法:
最小割经典模型。
所有格子向源点连权值为格子权值的边。
将棋盘黑白染色后白点反转源汇。
如果两个格子相邻那么黑点向白点连$inf$的有向边。
求最小割即可。
开始把所有点的权值都加上,如果被割掉那么就表示这个格子不选。
Code:
#include <bits/stdc++.h> #define inf 0x3f3f3f3f #define N 100010 using namespace std; queue<int>q; int to[N<<1],nxt[N<<1],tot=1,val[N<<1],head[N],dis[N],S,T; int d1[]={-1,1,0,0}; int d2[]={0,0,1,-1}; char *p1,*p2,buf[100000]; #define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++) int rd() {int x=0,f=1; char c=nc(); while(c<48) {if(c=='-') f=-1; c=nc();} while(c>47) x=(((x<<2)+x)<<1)+(c^48),c=nc(); return x*f;} inline void add(int x,int y,int z) { to[++tot]=y; val[tot]=z; nxt[tot]=head[x]; head[x]=tot; to[++tot]=x; val[tot]=0; nxt[tot]=head[y]; head[y]=tot; } bool bfs() { memset(dis,-1,sizeof dis); while(!q.empty()) q.pop(); dis[S]=0; q.push(S); while(!q.empty()) { int x=q.front(); q.pop(); for(int i=head[x];i;i=nxt[i]) if(dis[to[i]]==-1&&val[i]>0) { dis[to[i]]=dis[x]+1; q.push(to[i]); if(to[i]==T) return true; } } return false; } int dinic(int x,int fl) { int tmp=fl; if(x==T) return fl; for(int i=head[x];i;i=nxt[i]) if(dis[to[i]]==dis[x]+1&&val[i]>0) { int mdl=dinic(to[i],min(val[i],tmp)); if(!mdl) dis[to[i]]=-1; tmp-=mdl; val[i]-=mdl; val[i^1]+=mdl; if(!tmp) break; } return fl-tmp; } int main() { int ans=0; int n=rd(),m=rd(); T=n*m+1; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { int v=rd(); ans+=v; if((i+j)&1) add(S,(i-1)*m+j,v),add((i-1)*m+j,T,0); else add((i-1)*m+j,T,v),add(S,(i-1)*m+j,0); for(int k=0;k<4;k++) { int x=i+d1[k],y=j+d2[k]; if(x>=1&&x<=n&&y>=1&&y<=m) ((i+j)&1)? (add((i-1)*m+j,(x-1)*m+y,inf)): (add((x-1)*m+y,(i-1)*m+j,inf)); } } while(bfs()) ans-=dinic(S,1<<30); cout << ans << endl ; return 0; }
小结:最小割的建模还是比较具有规律性的。