BZOJ-1475: 方格取数

[文章目录]

Description

在一个n*n的方格里,每个格子里都有一个正整数。从中取出若干数,使得任意两个取出的数所在格子没有公共边,且取出的数的总和尽量大。n<=30

为毛我没有想到黑白染色啊。。。
发现黑白染色之后就只是两个集合了。那么转化为最小割,s向其中一个集合连边,流量为数值,另一个集合向t连边,流量为数值,相邻两点连边为inf,那么两个点至少有一个被割掉了。满足题意。

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
int n,s,t;
int head[1100],to[11000],nxt[11000],f[11000],cnt=1;
inline void add(int x,int y,int z)
{
    to[++cnt]=y; nxt[cnt]=head[x]; head[x]=cnt; f[cnt]=z;
    to[++cnt]=x; nxt[cnt]=head[y]; head[y]=cnt; f[cnt]=0;
}
queue<int>q;
int dis[1100];
bool bfs()
{
    memset(dis,-1,sizeof dis);
    while(!q.empty()) q.pop();
    q.push(s); dis[s]=0; int x;
    while(!q.empty())
    {
        x=q.front(); q.pop();
        for(int i=head[x];i;i=nxt[i]) if(f[i]>0&&dis[to[i]]==-1)
        {
            dis[to[i]]=dis[x]+1;
            if(to[i]==t) return true;
            q.push(to[i]);
        }
    }
    return false;
}
int dinic(int x,int flow)
{
    if(x==t) return flow;
    int xx,tmp=flow;
    for(int i=head[x];i;i=nxt[i]) if(f[i]>0&&dis[to[i]]==dis[x]+1)
    {
        xx=dinic(to[i],min(f[i],tmp));
        if(!xx) dis[to[i]]=-1;
        f[i]-=xx; f[i^1]+=xx; tmp-=xx;
        if(!tmp) break;
    }
    return flow-tmp;
}
int main()
{
    scanf("%d",&n); ll sum=0; int x; s=n*n+1; t=s+1;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
        {
            scanf("%d",&x);
            if((i+j)&1)  add(s,(i-1)*n+j,x);
            else add((i-1)*n+j,t,x);
            sum+=x;
        }
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j) if((i+j)&1)
        {
            if(i!=1) add((i-1)*n+j,(i-2)*n+j,inf);
            if(i!=n) add((i-1)*n+j,i*n+j,inf);
            if(j!=1) add((i-1)*n+j,(i-1)*n+j-1,inf);
            if(j!=n) add((i-1)*n+j,(i-1)*n+j+1,inf);
        }
    while(bfs()) sum-=dinic(s,inf);
    printf("%lld",sum);
    return 0;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注