原题链接:https://www.luogu.org/problemnew/show/P2774
在一个有 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 i n f ;超级源点向黑点连边,边权为点权;白点向超级汇点连边,边权为点权。
乖乖跑最小割。。。
#include
#define inf 0x3f3f3f3f
using namespace std;
const int M=2e4+5;
struct sd{int to,fl;}ed[M];
int start,end,id,n,m,sum,lay[M],itr[M];
vector<int>mmp[M];
queue<int>dui;
void add(int f,int t,int v)
{
if(f<=0||t<=0)return;
mmp[f].push_back(id);ed[id++]=(sd){t,v};
mmp[t].push_back(id);ed[id++]=(sd){f,0};
}
void in()
{
int a,pos;
scanf("%d%d",&n,&m);start=n*m+1;end=n*m+2;
for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)
{
scanf("%d",&a);pos=j+(i-1)*m;sum+=a;
if((i+j)%2)
{
add(start,pos,a);
if(j>1)add(pos,pos-1,inf);
if(j1,inf);
if(i>1)add(pos,pos-m,inf);
if(ielse add(pos,end,a);
}
}
bool bfs(int s,int e)
{
memset(lay,0,sizeof(lay));memset(itr,0,sizeof(itr));
lay[s]=1;dui.push(s);
int f,to,fl,hh;
while(!dui.empty())
{
f=dui.front();dui.pop();
for(int i=mmp[f].size()-1;i>=0;--i)
{
hh=mmp[f][i];to=ed[hh].to;fl=ed[hh].fl;
if(lay[to]||!fl)continue;
lay[to]=lay[f]+1;dui.push(to);
}
}
return lay[e];
}
int dfs(int s,int e,int mn)
{
if(s==e||!mn)return mn;
int ans=0,tmp,hh,to,fl,siz=mmp[s].size()-1;
for(int i=itr[s];i<=siz;++i)
{
hh=mmp[s][i];to=ed[hh].to;fl=ed[hh].fl;
if(lay[s]+1!=lay[to]||!fl)continue;
tmp=dfs(to,e,min(mn-ans,fl));
if(!tmp)continue;
ed[hh].fl-=tmp;ed[hh^1].fl+=tmp;
ans+=tmp;itr[s]=i;
if(mn==tmp)break;
}
return ans;
}
void ac()
{
int ans=0;
while(bfs(start,end))ans+=dfs(start,end,inf);
printf("%d",sum-ans);
}
int main(){in();ac();}