「LuoguP2774」 「网络流24题」 方格取数问题

Description


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

Input


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

Output


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

Sample Input


3 3
1 2 3
3 2 3
2 3 1

Sample Output


11

Hint


m,n<=100

题解


因为相邻两个数不能同时取的题设要求,像国际象棋棋盘一样染色 得到二分图

然后S向每个黑点连边,容量为点权

每个白点向T连边,容量为点权

最后每个黑点到能到的白点(上下左右)连边,容量为INF(表示这些边中需要割掉一些)

最后跑一遍最大流 利用最大流最小割定理可得此为最小割(最少要割掉的点权) ans=总点权-最小割

#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int INF=99999999;
struct emm{
    int e,f,v;
}a[1000007];
int h[10007];
int tot=1;
void con(int x,int y,int l)
{
    if(y==-1)return;
    a[++tot].f=h[x];
    h[x]=tot;
    a[tot].e=y;
    a[tot].v=l;
    a[++tot].f=h[y];
    h[y]=tot;
    a[tot].e=x;
    return;
}
int tu[107][107];
int num[107][107];
queue<int>q;
int d[10007];
int m,n,s,t;
bool bfs()
{
    memset(d,0,sizeof(d));
    d[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(int i=h[x];i;i=a[i].f)
        if(!d[a[i].e]&&a[i].v)
        {
            d[a[i].e]=d[x]+1;
            q.push(a[i].e);
        }
    }
    return d[t];
}
int dfs(int x,int al)
{
    if(x==t||!al)return al;
    int fl=0;
    for(int i=h[x];i;i=a[i].f)
    if(d[a[i].e]==d[x]+1&&a[i].v)
    {
        int f=dfs(a[i].e,min(a[i].v,al));
        if(f)
        {
            fl+=f;
            al-=f;
            a[i].v-=f;
            a[i^1].v+=f;
            if(!al)break;
        }
    }
    if(!fl)d[x]=-1;
    return fl;
}
int main()
{
    scanf("%d%d",&m,&n);
    s=0,t=m*n+1;
    int sum=0;
    for(int i=1;i<=m;++i)
    for(int j=1;j<=n;++j)
    {
        scanf("%d",&tu[i][j]);
        sum+=tu[i][j];
    }
    memset(num,-1,sizeof(num));
    int tim=0;
    for(int i=1;i<=m;++i)
    for(int j=1;j<=n;++j)
    num[i][j]=++tim;
    for(int i=1;i<=m;++i)
    for(int j=1;j<=n;++j)
    if((i+j)%2==0)con(num[i][j],t,tu[i][j]);
    else
    {
        con(s,num[i][j],tu[i][j]);
        con(num[i][j],num[i-1][j],INF);
        con(num[i][j],num[i+1][j],INF);
        con(num[i][j],num[i][j-1],INF);
        con(num[i][j],num[i][j+1],INF);
    }
    int ans=0;
    while(bfs())ans+=dfs(s,INF);
    cout<<sum-ans;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41007369/article/details/81016713
今日推荐