题目描述
在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。对于给定的方格棋盘,按照取数要求编程找出总和最大的数。
输入输出格式
输入格式:
第 1 行有 2 个正整数 m 和 n,分别表示棋盘的行数和列数。接下来的 m 行,每行有 n 个正整数,表示棋盘方格中的数。
输出格式:
程序运行结束时,将取数的最大总和输出
输入输出样例
输入样例
3 3
1 2 3
3 2 3
2 3 1
输出样例
11
说明
m,n<=100
分析
首先这题容易分析出是最小割问题(点集删去后图不连通且值最小)
然后想到奇偶建图
显然奇点会影响偶点(反之亦然)
所以源点连奇点,偶点连汇点,边权即为点权,然后两个可以相互影响的点建单向边,边权inf
#include <cstdio>
#include <queue>
#include <memory.h>
#define rep(i,a,b) for (i=a;i<=b;i++)
#define inf 2147483647
using namespace std;
struct edge{
int u,v,c,nx;
}g[140011];
int list[10011],d[10011],cur[10011];
int cnt=1;
int s,t;
int ans;
int n,m;
void Add(int u,int v,int w)
{
g[++cnt].u=u;g[cnt].v=v;g[cnt].c=w;g[cnt].nx=list[u];list[u]=cnt;
g[++cnt].u=v;g[cnt].v=u;g[cnt].c=0;g[cnt].nx=list[v];list[v]=cnt;
}
bool Bfs(){
queue<int> q;
int i;
memset(d,0,sizeof(d));
while (!q.empty()) q.pop();
q.push(s);
d[s]=1;
while (!q.empty()){
int x=q.front();q.pop();
i=list[x];
while (i>0){
if (g[i].c&&!d[g[i].v]){
d[g[i].v]=d[x]+1;
if (g[i].v==t) return 1;
q.push(g[i].v);
}
i=g[i].nx;
}
}
return 0;
}
int Mcf(int x,int minf){
int fpoint,fout=0;
if (x==t||!minf) return minf;
for (int &i=cur[x];i;i=g[i].nx)
if (d[x]+1==d[g[i].v]&&g[i].c){
fpoint=Mcf(g[i].v,min(g[i].c,minf-fout));
g[i].c-=fpoint;
g[i^1].c+=fpoint;
fout+=fpoint;
if (minf<=fout) break;
}
return fout;
}
void Dinic(){
int i;
while (Bfs()){
rep(i,1,n*m+2) cur[i]=list[i];
ans+=Mcf(s,inf);
}
}
int main()
{
int i,j;
int a[101][101],fans=0;
scanf("%d%d",&n,&m);
s=n*m+1;
t=n*m+2;
rep(i,0,n-1)
rep(j,1,m)
{
scanf("%d",&a[i][j]);
fans+=a[i][j];
if ((i+j)%2) a[i][j]=-a[i][j];
}
rep(i,0,n-1)
rep(j,1,m)
{
if (a[i][j]<0)
{
Add(s,i*m+j,-a[i][j]);
if (i-1>-1) Add(i*m+j,(i-1)*m+j,inf);
if (i+1<n) Add(i*m+j,(i+1)*m+j,inf);
if (j-1>0) Add(i*m+j,i*m+j-1,inf);
if (j+1<=m) Add(i*m+j,i*m+j+1,inf);
}
if (a[i][j]>0)
Add(i*m+j,t,a[i][j]);
}
Dinic();
printf("%d",fans-ans);
}