首页 > 其他分享 >【题解】P2774 方格取数问题

【题解】P2774 方格取数问题

时间:2022-12-12 11:57:03浏览次数:47  
标签:leq int 题解 取数 dep 方格 front P2774

方格取数问题

题目描述

有一个 \(m\) 行 \(n\) 列的方格图,每个方格中都有一个正整数。现要从方格中取数,使任意两个数所在方格没有公共边,且取出的数的总和最大,请求出最大的和。

输入格式

第一行是两个用空格隔开的整数,分别代表方格图的行数 \(m\) 和列数 \(n\)。

第 \(2\) 到第 \((m + 1)\) 行,每行 \(n\) 个整数,第 \((i + 1)\) 行的第 \(j\) 个整数代表方格图第 \(i\) 行第 \(j\) 列的的方格中的数字 \(a_{i, j}\)。

输出格式

输出一行一个整数,代表和最大是多少。

样例 #1

样例输入 #1

3 3
1 2 3
3 2 3
2 3 1

样例输出 #1

11

提示

数据规模与约定

对于 \(100\%\) 的数据,保证 \(1 \leq n, m \leq 100\),\(1 \leq a_{i, j} \leq 10^5\)。

提示

请注意输入的第一行先读入 \(m\) 再读入 \(n\)。

题解

话说其实转化一下题目,就变成“横行和纵行编号的和为奇数的和相邻的偶数的冲突”,考虑这种冲突问题,求和最大即求割最小,于是我们可以把所有点按上述奇偶分成两边,分别朝\(s\)和\(t\)连边(流量为权值),相邻的点连边,直接跑网络流最小割。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(w>'9'||w<'0'){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(w>='0'&&w<='9'){
		j=(j<<3)+(j<<1)+w-'0';
		w=getchar();
	}
	return f*j;
}
const int N=20001,M=100001;
int head[N],to[M],fro[M],len[M],tail=1;
int n,m,s,t,cnt,sum[101][101],X[4]={0,0,1,-1},Y[4]={1,-1,0,0};
int dep[N],fir[N],ans;
inline int turn(int x,int y){return (x-1)*m+y;}
inline void addline(int x,int y,int z){
	to[++tail]=y;
	fro[tail]=head[x];
	head[x]=tail;
	len[tail]=z;
	to[++tail]=x;
	fro[tail]=head[y];
	head[y]=tail;
	return ;
}
bool bfs(){
	for(int i=1;i<=cnt;i++)dep[i]=0;
	dep[s]=1;
	deque<int>p;
	p.push_front(s);
	while(!p.empty()){
		int u=p.front();p.pop_front();
		for(int k=head[u];k;k=fro[k]){
			int x=to[k];
			if(dep[x]||!len[k])continue;
			dep[x]=dep[u]+1;
			p.push_back(x);
		}
	}
	return dep[t];
}
int dfs(int u,int flo){
	if(u==t||!flo)return flo;
	int ansn=0;
	for(int k=fir[u];k;k=fro[k],fir[u]=k){
		int x=to[k];
		if(dep[x]<=dep[u]||!len[k])continue;
		int j=dfs(x,min(flo,len[k]));
		ansn+=j,flo-=j;
		len[k]-=j,len[k^1]+=j;
	}
	return ansn;
}
signed main(){
	n=rd(),m=rd();
	s=n*m+1,t=cnt=n*m+2;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			sum[i][j]=rd();
			ans+=sum[i][j];
			if((i+j)&1){
				addline(s,turn(i,j),sum[i][j]);
				for(int a=0;a<=3;a++){
					int x=i+X[a],y=j+Y[a];
					if(x<1||x>n||y<1||y>m)continue;
					addline(turn(i,j),turn(x,y),INT_MAX);
				}
			}
			else addline(turn(i,j),t,sum[i][j]);
		}
	}
	while(bfs()){
		for(int i=1;i<=cnt;i++)fir[i]=head[i];
		ans-=dfs(s,INT_MAX);
	}
	printf("%d",ans);
	return 0;
}

标签:leq,int,题解,取数,dep,方格,front,P2774
From: https://www.cnblogs.com/T-water/p/16975660.html

相关文章

  • 【题解】P3358 最长k可重区间集问题
    最长k可重区间集问题题目描述给定实直线\(\text{L}\)上\(n\)个开区间组成的集合\(\mathbf{I}\),和一个正整数\(k\),试设计一个算法,从开区间集合\(\mathbf{I}\)中选......
  • 洛谷 P1786 帮贡排序 题解
    原题链接P1786帮贡排序解析实现方法一看题:这不就是道排序吗?但是——用啥办法呢?这自带的排序方法,肯定是不能用了那么我们就来写一个cmp排序函数吧!但是——输出排......
  • 题解 洛谷 P2885 [USACO07NOV]Telephone Wire G
    1.题面描述题目链接给出\(n\)棵树的高度。你可以进行任意次操作,每次操作形如:把某棵树增高\(x\),代价为\(x^2\)(注意:不能将一棵树的高度减少)。在操作完之后,一个状态......
  • 题解 洛谷 P3594 [POI2015] WIL
    1.题面描述题目链接给定一个长度为\(n\)的序列,你有一次机会选中一段连续的长度不超过\(d\)的区间,将里面所有数字全部修改为\(0\)。请找到最长的一段连续区间,使得该......
  • 题解 CodeForces 940E Cashback
    1.题面描述题目链接给定长为\(n\)的序列和一个整数\(c\),你需要将其分为若干段。对于每一段,若其长度为\(k\),则可以删除其中前\(\lfloor\frac{k}{c}\rfloor\)小的......
  • 题解 洛谷 P3793 由乃救爷爷
    1.题面描述题目链接给定长为\(n\)的序列,\(m\)次询问,查询区间最大值。\(n,m\le10^7\),数据随机。2.分析经典的静态区间最小值问题,经典题目配经典算法,考虑到ST表......
  • NOIP2022 题解
    A.种花枚举\((x_2,y_0)\),考虑\(x_1\)可能在哪些位置,显然是在\(x_2\)往上的一个极长连续0段上。考虑如果固定了\(x_1\)的位置后怎么计算C形的数量,我们预处理......
  • CF1182E 题解
    前言题目传送门!更好的阅读体验?\(\text{zltqwq}\)推荐矩阵快速幂题目。思路......
  • P4902 乘积 题解
    乘积给出\(A\),\(B\),求下面的式子的值.\[\prod_{i=A}^{B}\prod_{j=1}^{i}(\frac{i}{j})^{\left\lfloor\frac{i}{j}\right\rfloor}\(\bmod\19260817)\]包含\(T\)组......
  • 【题解】CF1764C Doremy's City Construction
    题目传送门思路首先我们思考一个性质,由于不能有连续单调不升/不降的三个点连在一起,所以对于单个点来讲,显然要么只和比它大的连边(称为A类点),要么只和比它小的连边(称为B类点......