[最大流]方格取数问题

题目描述

在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。对于给定的方格棋盘,按照取数要求编程找出总和最大的数。

输入输出格式

输入格式:
第 1 行有 2 个正整数 m 和 n,分别表示棋盘的行数和列数。接下来的 m 行,每行有 n 个正整数,表示棋盘方格中的数。

输出格式:
程序运行结束时,将取数的最大总和输出

输入输出样例

输入样例
3 3
1 2 3
3 2 3
2 3 1
输出样例
11
说明

m,n<=100

分析

首先这题容易分析出是最小割问题(点集删去后图不连通且值最小)
然后想到奇偶建图
显然奇点会影响偶点(反之亦然)
所以源点连奇点,偶点连汇点,边权即为点权,然后两个可以相互影响的点建单向边,边权inf

#include <cstdio>
#include <queue>
#include <memory.h>
#define rep(i,a,b) for (i=a;i<=b;i++)
#define inf 2147483647
using namespace std;
struct edge{
    int u,v,c,nx;
}g[140011];
int list[10011],d[10011],cur[10011];
int cnt=1;
int s,t;
int ans;
int n,m;

void Add(int u,int v,int w)
{
    g[++cnt].u=u;g[cnt].v=v;g[cnt].c=w;g[cnt].nx=list[u];list[u]=cnt;
    g[++cnt].u=v;g[cnt].v=u;g[cnt].c=0;g[cnt].nx=list[v];list[v]=cnt;
}

bool Bfs(){
    queue<int> q;
    int i;
    memset(d,0,sizeof(d));
    while (!q.empty()) q.pop();
    q.push(s);
    d[s]=1;
    while (!q.empty()){
        int x=q.front();q.pop();
        i=list[x];
        while (i>0){
            if (g[i].c&&!d[g[i].v]){
                d[g[i].v]=d[x]+1;
                if (g[i].v==t) return 1;
                q.push(g[i].v);
            }
            i=g[i].nx;
        }
    }
    return 0;
}

int Mcf(int x,int minf){
    int fpoint,fout=0;
    if (x==t||!minf) return minf;
    for (int &i=cur[x];i;i=g[i].nx)
    if (d[x]+1==d[g[i].v]&&g[i].c){
        fpoint=Mcf(g[i].v,min(g[i].c,minf-fout));
        g[i].c-=fpoint;
        g[i^1].c+=fpoint;
        fout+=fpoint;
        if (minf<=fout) break;
    }
    return fout;
}

void Dinic(){
    int i;
    while (Bfs()){
        rep(i,1,n*m+2) cur[i]=list[i];
        ans+=Mcf(s,inf);
    }
}

int main()
{
    int i,j;
    int a[101][101],fans=0;
    scanf("%d%d",&n,&m);
    s=n*m+1;
    t=n*m+2;
    rep(i,0,n-1)
    rep(j,1,m)
    {
        scanf("%d",&a[i][j]);
        fans+=a[i][j];
        if ((i+j)%2) a[i][j]=-a[i][j];
    }
    rep(i,0,n-1)
    rep(j,1,m)
    {
        if (a[i][j]<0)
        {
            Add(s,i*m+j,-a[i][j]);
            if (i-1>-1) Add(i*m+j,(i-1)*m+j,inf);
            if (i+1<n) Add(i*m+j,(i+1)*m+j,inf);
            if (j-1>0) Add(i*m+j,i*m+j-1,inf);
            if (j+1<=m) Add(i*m+j,i*m+j+1,inf);
        }
        if (a[i][j]>0)
        Add(i*m+j,t,a[i][j]);

    }
    Dinic();
    printf("%d",fans-ans);
}

猜你喜欢

转载自blog.csdn.net/ssl_qyh0ice/article/details/80807385
今日推荐