首页 > 其他分享 >UVa10827 Maximum sum on a torus

UVa10827 Maximum sum on a torus

时间:2023-01-01 19:45:07浏览次数:41  
标签:int sum 矩阵 Maximum UVa10827 ans Front include torus

思路

一道比较经典的题目。

首先我们可以把方阵扩展一下,变成 \(4n^2\) 大小的方阵。

问题就变为在长度为 \(2\times n\) 的方阵中求一个长和宽均小于等于 \(n\) 的子矩阵的和的最大值。

方法一:预处理矩阵的二维前缀和,暴力枚举子矩阵的左上角和右下角并统计答案 时间复杂度 \(O(n^4)\) ,因为其他题解均有阐述,这里不再详细介绍。

方法二:我们可以只枚举矩形的上下边界,用其他方法来确定左右边界。

因为上下边界已经确定,我们可以把它压缩成一维的数组,问题就会变成最大子段和问题,这不是单调队列的拿手好戏吗?

设 \(f_i\) 为以 \(i\) 结尾的最大子段和,有方程: \(f_i = \max(sum_i - sum_j)(i- n\le j < i)\)。

\[f_i=sum_i-\min(sum_j)(i- n\le j < i) \]

用单调队列可以轻松维护,时间复杂度 \(O(n^3)\) 。

\(Code\)

#include <iostream>
#include <algorithm>
#include <cmath>
#include <string>
#include <cstring>
using namespace std;
const int N = 200 << 1;
int a[N][N];
int n, T;
int sum[N];
int f[N<<1];
int que[N<<1], Front, Back;
int s[N];
int dp(int *h){//单调队列求最大子段和
	int ans;
	Front = 1, Back = 1;
	que[1] = 1;ans = f[1] = s[1] = h[1];
	for(int i = 2; i <= n; i ++){
		s[i] = s[i - 1] + h[i];
		while(Front <= Back and i - que[Front] > n/2)Front ++;
		f[i] = s[i] - s[que[Front]];
		ans = max(ans, f[i]);
		while(Front <= Back and s[que[Back]] >= s[i])Back--;
		que[++Back] = i;
	}
	return ans;
}
int main(){
//	freopen("text.in", "r", stdin);
//	freopen("text.out", "w", stdout);
	scanf("%d", &T);
	while(T--){
		int ans = -0x3f3f3f3f;
		scanf("%d", &n);
		//把矩阵括大两倍
		for(int i = 1; i <= n; i ++)
			for(int j = 1; j <= n; j ++)
				scanf("%d", &a[i][j]), a[i][j + n] = a[i][j],
				a[i + n][j] = a[i + n][j + n] = a[i][j];
		n <<= 1;
		for(int i = 1; i <= n; i ++){
			memset(sum, 0, sizeof sum);
			for(int j = i; j <= n and j - i + 1 <= n / 2; j ++){
				for(int k = 1; k <= n; k ++)
					sum[k] += a[j][k];//压缩,可以由 [i, j - 1]递推得到
				ans = max(ans, dp(sum));
			}
		}
		printf("%d\n", ans);
	}
	return 0;
}

标签:int,sum,矩阵,Maximum,UVa10827,ans,Front,include,torus
From: https://www.cnblogs.com/dadidididi/p/17018474.html

相关文章