9.方格取数问题
题目描述 Description
在一个有m*n个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意2个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。
输入描述 Input Description
第1行有2个正整数m和n,分别表示棋盘的行数和列数。接下来的m行,每行有n个正整数,表示棋盘方格中的数。
输出描述 Output Description
将取数的最大总和输出
样例输入 Sample Input
3 3
1 2 3
3 2 3
2 3 1
样例输出 Sample Output
11
分析: 选与不选问题,用最小割。将图二分,显然不可能相邻的放在一起,在这两个集合中间,将可能相邻的连边,最后将图割开后左边集合和右边集合就没有边相接了,也就是没有相邻的数被选了
建图: s向i%2-j%2==0的点连一条容量为点权的边,t向其他点连一条容量为点权的边,每个点向相邻的点连一条容量为INF的边(不可能割这条边的)
答案就是所有点权值和-最小割(最大流)
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 2100000000
struct node {
int to;
int next;
int flow;
}e[100000];
int f[10000],d[10000];
int head[10000],cur[10000];
int n,m,s,t,tot=1;
int read() {
int ans=0,flag=1;
char ch=getchar();
while( (ch>'9' || ch<'0') && ch!='-' ) ch=getchar();
if(ch=='-') flag=-1,ch=getchar();
while(ch>='0' && ch<='9') ans=ans*10+ch-'0',ch=getchar();
return ans*flag;
}
void addedge(int u,int v,int w) {
e[++tot].to=v;
e[tot].flow=w;
e[tot].next=head[u];
head[u]=tot;
e[++tot].to=u;
e[tot].flow=0;
e[tot].next=head[v];
head[v]=tot;
return ;
}
int dfs(int now,int flow) {
if(now==t)
return flow;
int use=0;
for(int i=cur[now];i;i=e[i].next) {
cur[now]=i;
if(d[e[i].to]+1==d[now] && e[i].flow>0) {
int temp=dfs(e[i].to,min(flow-use,e[i].flow));
use+=temp;
e[i].flow-=temp;
e[i^1].flow+=temp;
if(flow==use)
return use;
}
}
cur[now]=head[now];
if(!(--f[d[now]]))
d[s]=n*m+2;
++f[++d[now]];
return use;
}
int main(){
int ans=0;
n=read(),m=read();
s=0;t=n*m+1;
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
int w=read();
ans+=w;
if(i%2-j%2==0) {
addedge(s,(i-1)*m+j,w);
if(j!=1)
addedge((i-1)*m+j,(i-1)*m+j-1,INF);
if(j!=m)
addedge((i-1)*m+j,(i-1)*m+j+1,INF);
if(i!=1)
addedge((i-1)*m+j,(i-2)*m+j,INF);
if(i!=n)
addedge((i-1)*m+j,i*m+j,INF);
}
else
addedge((i-1)*m+j,t,w);
}
}
f[0]=n*m+2;
while(d[s]<n*m+2)
ans-=dfs(s,1<<30);
printf("%d",ans);
return 0;
}