Description
在一个有$m; imes;n$个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意$2$个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。
Input
第$1$行有$2$个正整数$m,n$,分别表示棋盘的行数和列数。
接下来的$m$行,每行有$n$个正整数,表示棋盘方格中的数。
Output
一行一个整数,表示取数的最大总和.
Sample Input
3 3
1 2 3
3 2 3
2 3 1
Sample Output
11
HINT
$n,m<50$.
Solution
将棋盘黑白染色,使相邻格子颜色不同,黑色格子为集合$x$中的点,白色格子为集合$y$中的点.
从$s$向$x_i$连一条容量为格子中数值的有向边.
从$x_i$向$y_j$连一条容量为$+infty$的有向边($x_i,y_j$有公共边).
从$y_i$向$t$连一条容量为格子中数值的有向边.
求最大点权独立集.
最大点权独立集=总点权-最小点权覆盖=总点权-最小割=总点权-最大流。
#include<cmath> #include<ctime> #include<queue> #include<stack> #include<cstdio> #include<vector> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #define M 4 #define N 50 using namespace std; struct graph{ int nxt,to,f; }e[N*N]; int a[N][N],b[N][N],g[N*N],dep[N*N],n,m,s,t,cnt,sum; int x[M]={0,0,-1,1},y[M]={-1,1,0,0}; queue<int> q; inline void addedge(int x,int y,int f){ e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;e[cnt].f=f; } inline void adde(int x,int y,int f){ addedge(x,y,f);addedge(y,x,0); } inline bool bfs(int u){ memset(dep,0,sizeof(dep)); dep[u]=1;q.push(u); while(!q.empty()){ u=q.front();q.pop(); for(int i=g[u];i;i=e[i].nxt) if(e[i].f>0&&!dep[e[i].to]){ q.push(e[i].to); dep[e[i].to]=dep[u]+1; } } return dep[t]; } inline int dfs(int u,int f){ int ret=0; if(u==t) return f; for(int i=g[u],d;i&&f;i=e[i].nxt) if(e[i].f>0&&dep[e[i].to]>dep[u]){ d=dfs(e[i].to,min(f,e[i].f)); e[i].f-=d;e[i^1].f+=d;ret+=d;f-=d; } return ret; } inline int dinic(){ int ret=0; while(true){ if(!bfs(s)) return ret; ret+=dfs(s,sum); } } inline void Aireen(){ scanf("%d%d",&n,&m); for(int i=0;i<n;++i) for(int j=0;j<m;++j){ scanf("%d",&a[i][j]); sum+=a[i][j];b[i][j]=++cnt; } cnt=1;s=n*m+1;t=s+1; for(int i=0;i<n;++i) for(int j=i&1;j<m;j+=2){ adde(s,b[i][j],a[i][j]); for(int k=0,u,v;k<M;++k){ u=i+x[k];v=j+y[k]; if(u>=0&&u<n&&v>=0&&v<m) adde(b[i][j],b[u][v],sum); } } for(int i=0;i<n;++i) for(int j=i&1^1;j<m;j+=2) adde(b[i][j],t,a[i][j]); printf("%d ",sum-dinic()); } int main(){ freopen("grid.in","r",stdin); freopen("grid.out","w",stdout); Aireen(); fclose(stdin); fclose(stdout); return 0; }