正题
链接:
https://www.luogu.org/problemnew/show/P2774
题意
在一个n*m的数字矩阵中取数,取得数不能相邻,求能取到的最大价值。
解题思路
最大价值,那么反着去想,就是取若干个格子,让所有格子的都不相邻,要求权最小,那么就是最小割问题。然后二分建图。起点与奇数点连容量为该点价值的边,偶数点与终点连容量为该点价值的边,然后相邻的连一条容量无限的边,这样就不会割去这条边,然后所有的权值和减去最小割就是答案。
代码
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
struct line{
int to,next,c;
}a[200001];
queue<int>f;//队列
int n,d[10055],s,e,m,answ,w,tot,ls[10055];
void addl(int x,int y,int z)//加边
{
a[++tot].to=y;a[tot].next=ls[x];a[tot].c=z;ls[x]=tot;
}
bool bfs()//在残量网上建分层图
{
memset(d,0,sizeof(d));
d[s]=1;f.push(s);
while (!f.empty())
{
int x=f.front();
for (int i=ls[x];i>=0;i=a[i].next)
{
int y=a[i].to;
if (a[i].c>0 && d[y]==0)
{
d[y]=d[x]+1;
f.push(y);
}
}
f.pop();
}
if (d[e]) return true;
else return false;
}
int dinic(int x,int flow)//求最大流
{
int rest=0,k;
if (x==e) return flow;
for (int i=ls[x];i>=0;i=a[i].next)
{
int y=a[i].to;
if (a[i].c>0&&d[y]==d[x]+1&&flow>rest)
{
rest+=(k=(dinic(y,min(flow-rest,a[i].c))));
a[i].c-=k;
a[i^1].c+=k;
}
}
if (!rest) d[x]=0;
return rest;
}
int ansq()
{
int sum=0;
while (bfs())
sum+=dinic(s,2147483647);//统计值
return sum;
}
int main()
{
int x;tot=-1;
scanf("%d%d",&n,&m);
s=0;e=n*m+1;
memset(ls,-1,sizeof(ls));
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
scanf("%d",&w);
answ+=w;
int num=(i-1)*m+j;//连边
if ((i+j)&1) addl(s,num,w),addl(num,s,0);
else addl(num,e,w),addl(e,num,0);
}
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if ((i+j)&1)
{
for (int k=0;k<4;k++)
{
int x1=i+dx[k],y1=j+dy[k];//相邻连边
if (x1<=0 || x1>n || y1<=0 || y1>m) continue;
int num=(i-1)*m+j;
addl(num,(x1-1)*m+y1,2147483647);
addl((x1-1)*m+y1,num,0);
}
}
printf("%d",answ-ansq());//输出
}