洛谷 P2774 方格取数问题 最小割

本文介绍了一种解决二分图最大权独立集问题的算法实现,通过构建网络流图并使用Dinic算法求解最小割,进而得到最大权独立集的值。适用于在特定条件下从棋盘格中选取数值的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

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

输入输出格式

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

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

输入输出样例

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

m,n<=100

分析:一道二分图的最大权独立集。显然给图进行黑白染色(即一种颜色与周围的颜色都不同),满足二分图性。那显然如果我们选了一个点,就不能选周围点。也就是源点向一个白点连一条白点权值的边,白点向黑点连inf的边,黑点向汇点连黑点权值的边。显然就是一个最小割了。于是可以先把所有点权加起来,然后减去最小割就是答案。

dalao说他也不会一般图的最大权独立集,不过一般图的最大独立集可以用带花树做(什么东西呀)。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>

const int maxn=105;
const int inf=0x3f3f3f3f;

using namespace std;

int n,m,i,j,ans,cnt,s,t;
int a[maxn][maxn],ls[maxn*maxn],dis[maxn*maxn];

struct node{
    int y,next,op,c;
}g[maxn*maxn*8];

queue <int> q;

void add(int x,int y,int w)
{
    g[++cnt].y=y; g[cnt].c=w; g[cnt].op=cnt+1; g[cnt].next=ls[x]; ls[x]=cnt; 
    g[++cnt].y=x; g[cnt].c=0; g[cnt].op=cnt-1; g[cnt].next=ls[y]; ls[y]=cnt;
}

int fc(int x,int y)
{
    return (x-1)*m+y;
}

bool bfs()
{
    for (int i=s;i<=t;i++) dis[i]=0;
    dis[s]=1;
    while (!q.empty()) q.pop();
    q.push(s);
    while (!q.empty())
    {
        int u=q.front();
        q.pop();
        for (int i=ls[u];i>0;i=g[i].next)
        {
            int v=g[i].y;
            if ((dis[v]==0) && (g[i].c))
            {
                dis[v]=dis[u]+1;
                if (v==t) return true;
                q.push(v);
            }
        }
    }
    return false;
}

int dfs(int x,int maxf)
{
    if ((x==t) || (maxf==0)) return maxf;
    int ret=0;
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if ((dis[x]+1==dis[y]) && (g[i].c))
        {
            int f=dfs(y,min(g[i].c,maxf-ret));
            g[i].c-=f;
            g[g[i].op].c+=f;
            ret+=f;
        }
    }
    return ret;
}

void dinic()
{
    while (bfs()) ans-=dfs(s,inf);
}
int main()
{
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;i++)
    {
        for (j=1;j<=m;j++)
        {
            scanf("%d",&a[i][j]);
            ans+=a[i][j];
        }
    }   
    s=0; t=n*m+1;
    for (i=1;i<=n;i++)
    {
        for (j=1;j<=m;j++)
        {
            if ((i+j)%2)
            {
                add(s,fc(i,j),a[i][j]);
                if (i>1) add(fc(i,j),fc(i-1,j),inf);
                if (i<n) add(fc(i,j),fc(i+1,j),inf);
                if (j>1) add(fc(i,j),fc(i,j-1),inf);
                if (j<m) add(fc(i,j),fc(i,j+1),inf);
            }
            else add(fc(i,j),t,a[i][j]);
        }
    }   
    dinic();
    printf("%d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值