正题
链接:
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());//输出
}