TZOJ 3665 方格取数(2)(最大点权独立集)

时间:2021-07-22 08:17:42

描述

给你一个m*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的数的和最大。

输入

包括多个测试实例,每个测试实例包括2整数m,n和m行n列的非负数(m<=50,n<=50)

输出

对于每个测试实例,输出可能取得的最大的和

样例输入

3 3
75 15 21
75 15 28
34 70 5

样例输出

188

题意

如上

题解

最大点权独立集=总权值-最小点权覆盖

用最小割跑出来的是最小点权覆盖,再用sum-最小点权覆盖

把图黑白染色,黑色点连源点S流量a[i][j],白色点连汇点T流量a[i][j],然后每个相邻的黑白点连黑白边流量INF,跑最小割即可

代码

 #include<bits/stdc++.h>
using namespace std; const int maxn=1e5+;
const int maxm=2e5+;
int n,m,S,T;
int deep[maxn],q[];
int FIR[maxn],TO[maxm],CAP[maxm],COST[maxm],NEXT[maxm],tote; void add(int u,int v,int cap)
{
TO[tote]=v;
CAP[tote]=cap;
NEXT[tote]=FIR[u];
FIR[u]=tote++; TO[tote]=u;
CAP[tote]=;
NEXT[tote]=FIR[v];
FIR[v]=tote++;
}
bool bfs()
{
memset(deep,,sizeof deep);
deep[S]=;q[]=S;
int head=,tail=;
while(head!=tail)
{
int u=q[++head];
for(int v=FIR[u];v!=-;v=NEXT[v])
{
if(CAP[v]&&!deep[TO[v]])
{
deep[TO[v]]=deep[u]+;
q[++tail]=TO[v];
}
}
}
return deep[T];
}
int dfs(int u,int fl)
{
if(u==T)return fl;
int f=;
for(int v=FIR[u];v!=-&&fl;v=NEXT[v])
{
if(CAP[v]&&deep[TO[v]]==deep[u]+)
{
int Min=dfs(TO[v],min(fl,CAP[v]));
CAP[v]-=Min;CAP[v^]+=Min;
fl-=Min;f+=Min;
}
}
if(!f)deep[u]=-;
return f;
}
int maxflow()
{
int ans=;
while(bfs())
ans+=dfs(S,<<);
return ans;
}
void init()
{
tote=;
memset(FIR,-,sizeof FIR);
}
int N,M,a[][],color[][],has[][],tot,sum,f;
int main()
{
while(cin>>N>>M)
{
init();
memset(color,,sizeof color);
tot=sum=;
for(int i=;i<=N;i++)
{
if(i&)f=;
else f=-;
for(int j=;j<=M;f*=-,j++)
{
scanf("%d",&a[i][j]);
sum+=a[i][j];
has[i][j]=tot++;
color[i][j]=f;
}
}
S=tot,T=S+;
for(int i=;i<=N;i++)
for(int j=;j<=M;j++)
{
if(color[i][j]==)
add(S,has[i][j],a[i][j]);
else
add(has[i][j],T,a[i][j]);
if(color[i-][j]==-)add(has[i][j],has[i-][j],<<);
if(color[i+][j]==-)add(has[i][j],has[i+][j],<<);
if(color[i][j-]==-)add(has[i][j],has[i][j-],<<);
if(color[i][j+]==-)add(has[i][j],has[i][j+],<<);
}
cout<<sum-maxflow()<<'\n';
}
return ;
}