【P2774】方格取数(网络流24)

本文介绍了一种在棋盘上选取不相邻方格中的数以获得最大总和的算法。通过构建图模型并使用最小割算法求解,最终输出最大总和。

摘要生成于 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





题解:

    我们考虑这一个n*m的方格,我们首先对其进行黑白染色,从s连到黑色的点流量为点权,再从黑色的点连到白色的点流量为INF,然后再从白色的点连到t,流量也为点权。我们考虑一下,我们找出最小割,因为我们是把有公共边的两个点建的图,然后用总的减去最小割就是答案;

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
#define INF 2147483647
const int MAXN=1e6;
int mx[4]={0,0,1,-1};
int my[4]={1,-1,0,0};
int level[MAXN],n,m,head[MAXN],val,Total,cnt,s,t;
using namespace std;
struct Edge
{
	int next,to,c,f;
}edge[MAXN];
inline void Add_Edge(int u,int v,int cc,int ff)
{
    edge[cnt]=(Edge){head[u],v,cc,ff};
    head[u]=cnt++;
} 
bool Bfs(int s,int t)
{
    memset(level,0,sizeof(level));
    level[s]=1;
    queue<int> que;
    que.push(s);
    while(!que.empty())
    {
        int c=que.front();
        que.pop();
        for(int i=head[c];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(level[v]==0&&edge[i].c>edge[i].f)
            {
                level[v]=level[c]+1;
                que.push(v);
            }
        }
    }
    if(level[t]==0) return 0;
    else return 1;
}
int Dfs(int u,int f)
{
    if(u==t) return f;
    int adf=0;
    for(int i=head[u];i!=-1&&adf<f;i=edge[i].next)
    {
        int v=edge[i].to;
        if(level[v]==level[u]+1&&edge[i].c>edge[i].f)
        {
            int temp=Dfs(v,min(f-adf,edge[i].c-edge[i].f));
            edge[i].f+=temp;
            edge[i^1].f-=temp;
            adf+=temp;
        } 
    }
    return adf;
}
int Dinic(int s,int t)
{
    int sumflow=0;
    while(Bfs(s,t))
    {
        sumflow+=Dfs(s,INF);
    }
    return sumflow;
}
int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	s=0,t=n*m+1;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			int val;
			scanf("%d",&val);
			Total+=val;
			if(((i+j))%2==0) Add_Edge(s,(i-1)*m+j,val,0),Add_Edge((i-1)*m+j,s,0,0);
			else Add_Edge((i-1)*m+j,t,val,0),Add_Edge(t,(i-1)*m+j,0,0);
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if((i+j)%2==0)
			for(int k=0;k<4;k++)
			{	
				int nowx=i+mx[k];
				int nowy=j+my[k];
				if(nowx>=1&&nowx<=n&&nowy>=1&&nowy<=m)
				{
					Add_Edge((i-1)*m+j,(nowx-1)*m+nowy,1e8,0);
					Add_Edge((nowx-1)*m+nowy,(i-1)*m+j,0,0);
				}
			}
		}
	}
	printf("%d",Total-Dinic(s,t));
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值