传送门:洛谷-方格取数问题
题意
在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。对于给定的方格棋盘,按照取数要求编程找出总和最大的数。
数据范围
m,n<=100
题解
说一下本蒟蒻做这道题的历程。
嗯,一道最大流问题。
建模应该可以选择每个点,和与它没有公共边的点之间连一条边,然而顺次跑最大流。
可是有一个问题,这条边的权值怎么定义,而且
n2
n
2
遍历太窒息了。
那么我们再思考一下,可不可以在每个点与它有公共边的点之间连一条有向边?但如果不分点的情况,我们又让哪些点与源点,哪些点与汇点相连呢?
于是可以考虑,我们将棋子分个类,先不考虑每个点的权值,我们一次能选取的最多的棋子个数是不超过n*m/2个的(若n,m均为奇数,则不超过n*m/2+1个)。而选取方法是对于第i行第j列的数,将i,j奇偶相同的分为一组,i,j奇偶不同的分为一组。这样就可以保证两个组内的点都没有公共边啦。
我们先选择其中一组的全部点,把它们与源点之间连一条为该点权值大小的有向边,再将它们与第二组中和它们有公共边的周围的点之间,连一条权值无穷大的边,然后选择另一组的全部点,把它们和汇点之间连一条为该点权值大小的有向边。
这里运用的知识本蒟蒻才发现:这是一个最小割问题,我们逆向思维先选取所有点,然后跑出最小点权覆盖,减去这些点权之和,得到的就是答案。
本蒟蒻上网搜了搜,发现是这么回事:
最大点独立集=总权-最小点权覆盖集
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define INF 0x7fffffff
using namespace std;
typedef long long ll;
const int N=1e4+10;
const int M=1e7+10;
int n,m,T;ll ans=0;
int val,f[102][102];
int head[N],to[M],w[M],flow[M],nxt[M],tot,d[N];
queue<int>Q;
inline int min(int x,int y)
{
return x>y? y:x;
}
inline void lk(int u,int v,int c)
{
to[tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot++]=c;
to[tot]=u;nxt[tot]=head[v];head[v]=tot;w[tot++]=0;
}
inline void pre()
{
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
T=n*m+1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
scanf("%d",&f[i][j]);
ans+=f[i][j];
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
int now=(i-1)*m+j;
if((i&1)^(j&1) == 1){
lk(now,T,f[i][j]);
}else{
lk(0,now,f[i][j]);
if(j>1) lk(now,now-1,INF);
if(i>1) lk(now,now-m,INF);
if(j<m) lk(now,now+1,INF);
if(i<n) lk(now,now+m,INF);
}
}
}
inline bool bfs()
{
memset(d,-1,sizeof(d));
d[0]=0;Q.push(0);
while(!Q.empty()){
int now=Q.front();Q.pop();
for(int i=head[now];i!=-1;i=nxt[i]){
if(d[to[i]]==-1 && w[i]>flow[i]){
d[to[i]]=d[now]+1;
Q.push(to[i]);
}
}
}
if(d[T]==-1) return false;
return true;
}
inline int dfs(int st,int ed,int f)
{
if(st==ed) return f;
int sum=0;
for(int i=head[st];i!=-1;i=nxt[i]){
if(d[to[i]]==d[st]+1 && w[i]>flow[i]){
int ww=f-sum;
int qw=dfs(to[i],ed,min(ww,w[i]-flow[i]));
if(qw){
flow[i]+=qw;
flow[i^1]-=qw;
sum+=qw;
if(sum==f) return f;
}
}
}
if(sum==0) d[st]=-1;
return sum;
}
inline void get()
{
while(bfs()){
int now=dfs(0,T,INF);
// printf("here:%d\n",now);
ans-=now;
}
printf("%lld\n",ans);
}
int main(){
pre();get();
return 0;
}