[题解] [网络流二十四题(九)] 方格取数问题(最小割)

9.方格取数问题

题目描述 Description
在一个有m*n个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意2个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。
输入描述 Input Description
第1行有2个正整数m和n,分别表示棋盘的行数和列数。接下来的m行,每行有n个正整数,表示棋盘方格中的数。
输出描述 Output Description
将取数的最大总和输出
样例输入 Sample Input
3 3
1 2 3
3 2 3
2 3 1
样例输出 Sample Output
11

分析: 选与不选问题,用最小割。将图二分,显然不可能相邻的放在一起,在这两个集合中间,将可能相邻的连边,最后将图割开后左边集合和右边集合就没有边相接了,也就是没有相邻的数被选了
建图: s向i%2-j%2==0的点连一条容量为点权的边,t向其他点连一条容量为点权的边,每个点向相邻的点连一条容量为INF的边(不可能割这条边的)
答案就是所有点权值和-最小割(最大流)

Code:

#include <bits/stdc++.h>
using namespace std;
#define INF 2100000000
struct node {
    int to;
    int next;
    int flow;
}e[100000];
int f[10000],d[10000];
int head[10000],cur[10000];
int n,m,s,t,tot=1;
int read() {
    int ans=0,flag=1;
    char ch=getchar();
    while( (ch>'9' || ch<'0') && ch!='-' ) ch=getchar();
    if(ch=='-') flag=-1,ch=getchar();
    while(ch>='0' && ch<='9') ans=ans*10+ch-'0',ch=getchar();
    return ans*flag;
}
void addedge(int u,int v,int w) {
    e[++tot].to=v;
    e[tot].flow=w;
    e[tot].next=head[u];
    head[u]=tot;

    e[++tot].to=u;
    e[tot].flow=0;
    e[tot].next=head[v];
    head[v]=tot;
    return ;
}
int dfs(int now,int flow) {
    if(now==t)
        return flow;
    int use=0;
    for(int i=cur[now];i;i=e[i].next) {
        cur[now]=i;
        if(d[e[i].to]+1==d[now] && e[i].flow>0) {
            int temp=dfs(e[i].to,min(flow-use,e[i].flow));
            use+=temp;
            e[i].flow-=temp;
            e[i^1].flow+=temp;
            if(flow==use)
                return use;
        }
    }
    cur[now]=head[now];

    if(!(--f[d[now]]))
        d[s]=n*m+2;
    ++f[++d[now]];

    return use;
}
int main(){
    int ans=0;
    n=read(),m=read();
    s=0;t=n*m+1;
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=m;j++) {
            int w=read();
            ans+=w;
            if(i%2-j%2==0) {
                addedge(s,(i-1)*m+j,w); 
                if(j!=1)
                    addedge((i-1)*m+j,(i-1)*m+j-1,INF);
                if(j!=m)
                    addedge((i-1)*m+j,(i-1)*m+j+1,INF);
                if(i!=1)
                    addedge((i-1)*m+j,(i-2)*m+j,INF);
                if(i!=n)
                    addedge((i-1)*m+j,i*m+j,INF);
            }
            else
                addedge((i-1)*m+j,t,w);
        }
    }
    f[0]=n*m+2;
    while(d[s]<n*m+2)
        ans-=dfs(s,1<<30);
    printf("%d",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值