题目
在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大。
分析
匈牙利算法做二分图匹配,是OK的,但是网络流更高效,按奇偶性建图,再建立源点和汇点,分别连接,跑一遍最大流。
代码
#include <cstdio>
#include <cctype>
#include <queue>
using namespace std;
struct node{int y,next,w;}e[60001];
const int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
int k=1,n,m,ans,s,t,ls[10001],dis[10001];
int in(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
void add(int x,int y,int w){
e[++k].y=y; e[k].w=w; e[k].next=ls[x]; ls[x]=k;
e[++k].y=x; e[k].w=0; e[k].next=ls[y]; ls[y]=k;
}
bool bfs(){
for (int i=1;i<=n*m+2;i++) dis[i]=0;
queue<int>q; q.push(s); dis[s]=1;
while (q.size()){
int x=q.front(); q.pop();
for (int i=ls[x];i;i=e[i].next)
if (e[i].w>0&&!dis[e[i].y]){
dis[e[i].y]=dis[x]+1;
if (e[i].y==t) return 1;
q.push(e[i].y);
}
}
return 0;
}
int min(int a,int b){return (a<b)?a:b;}
int dfs(int x,int now){
if (x==t||!now) return now;
int rest=0,f;
for (int i=ls[x];i;i=e[i].next)
if (e[i].w>0&&dis[e[i].y]==dis[x]+1){
rest+=(f=dfs(e[i].y,min(now-rest,e[i].w)));
e[i].w-=f; e[i^1].w+=f;
if (now==rest) return rest;
}
if (!rest) dis[x]=0;
return rest;
}
int main(){
n=in(); m=in(); s=n*m+1,t=n*m+2;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++){
int p=in(); ans+=p;
if (!((i+j)&1)) add((i-1)*m+j,t,p);
if ((i+j)&1){
add(s,(i-1)*m+j,p);
for (int o=0;o<4;o++){
int x=i+dx[o],y=j+dy[o];
if (x<1||x>n||y<1||y>m) continue;
add((i-1)*m+j,(x-1)*m+y,1e8);
}
}
}
while (bfs()) ans-=dfs(s,1e9);
return !printf("%d",ans);
}