【BZOJ3232】圈地游戏
题面
http://darkbzoj.tk/problem/3232
题解
非常神仙的一道题。(跟$yyb$的评价一样)
这道题其实不是最大权闭合子图。是一个普通的最小割。
首先,分数规划,二分答案。
其次,先把所有格子的权值都加上,
让$S$流向每个格子,边权为$V[i][j]$,然后我们在各格子间连边,权值为格线的权。
拿$sum-flow$就是$V-mid \times c$的最大值。
因为这样可以表达这样的关系:想留下每个格子,就要割掉它周围的边(或它周围的格子),若割掉的联通块中有格子被割了,不如不割。
但是这样没有汇点,求最小割是没有意义的。
考虑限制,取到外面一定不合法。
让$T$代表外面,让每个边缘的格子向$T$连边,边权为对应格线的权。求最小割就行了。
#include<queue> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define N 5050 #define INF 1000000007 #define ri register int #define db double #define eps 1e-5 using namespace std; int n,m; int a[100][100],b[100][100],c[100][100]; struct graph { #define S 0 #define T (n*m+1) vector<db> w; vector<int> to,ed[N]; int d[N],cur[N]; void add_edge(int a,int b,db tw) { w.push_back(tw); to.push_back(b); ed[a].push_back(to.size()-1); w.push_back(0); to.push_back(a); ed[b].push_back(to.size()-1); } void add_edges(int a,int b,db tw) { w.push_back(tw); to.push_back(b); ed[a].push_back(to.size()-1); w.push_back(tw); to.push_back(a); ed[b].push_back(to.size()-1); } void clear() { for (ri i=S;i<=T;i++) ed[i].clear(); to.clear(); w.clear(); } bool bfs() { memset(d,0x3f,sizeof(d)); queue<int> q; d[S]=0; q.push(0); while (!q.empty()) { int x=q.front(); q.pop(); for (ri i=0,l=ed[x].size();i<l;i++) { int e=ed[x][i]; if (d[to[e]]>d[x]+1 && w[e]-eps>0) { d[to[e]]=d[x]+1; q.push(to[e]); } } } return d[T]<INF; } db dfs(int x,db limit) { if (x==T) return limit; db tot=0; for (ri &i=cur[x];i<ed[x].size();i++) { int e=ed[x][i]; if (d[x]+1==d[to[e]] && w[e]-eps>0) { db f=dfs(to[e],min(limit,w[e])); if (f-eps<=0) continue; w[e]-=f; w[1^e]+=f; tot+=f; limit-=f; if (limit-eps<=0) return tot; } } return tot; } db dinic() { db ret=0; while (bfs()) { memset(cur,0,sizeof(cur)); ret+=dfs(S,INF); } return ret; } } G; bool check(db mid) { G.clear(); db sum=0; for (ri i=1;i<=n;i++) { for (ri j=1;j<=m;j++) { G.add_edge(S,(i-1)*m+j,a[i][j]); sum+=a[i][j]; } } for (ri i=1;i<=n+1;i++) { for (ri j=1;j<=m;j++) { if (i==1) G.add_edge(j,T,b[i][j]*mid); else if (i==n+1) G.add_edge((n-1)*m+j,T,b[i][j]*mid); else G.add_edges((i-2)*m+j,(i-1)*m+j,b[i][j]*mid); } } for (ri i=1;i<=n;i++) { for (ri j=1;j<=m+1;j++) { if (j==1) G.add_edge((i-1)*m+1,T,c[i][j]*mid); else if (j==m+1) G.add_edge((i-1)*m+m,T,c[i][j]*mid); else G.add_edges((i-1)*m+j-1,(i-1)*m+j,c[i][j]*mid); } } if (sum-G.dinic()>0) return 1; else return 0; } int main() { scanf("%d %d",&n,&m); for (ri i=1;i<=n;i++) for (ri j=1;j<=m;j++) scanf("%d",&a[i][j]); for (ri i=1;i<=n+1;i++) for (ri j=1;j<=m;j++) scanf("%d",&b[i][j]); for (ri i=1;i<=n;i++) for (ri j=1;j<=m+1;j++) scanf("%d",&c[i][j]); db lb=0,rb=407.0,ans; while (rb-lb>1e-5) { db mid=(lb+rb)/2; if (check(mid)) ans=lb=mid; else rb=mid; } printf("%.3lf",ans); return 0; }