我决定用kadane、枚举和压缩来做这题
2025-12-11 23:36:01
发布于:广东
【题解】最大加权矩形 —— 枚举上下界 + Kadane 降维法
核心思想:把二维最大子矩阵问题,转化为多个一维最大子段和问题!
先看完整代码
#include <bits/stdc++.h>
using namespace std;
// Kadane 算法:求一维数组的最大子段和
long long kadane(const vector<long long>& arr) {
long long maxEndingHere = arr[0]; // 以当前元素结尾的最大和
long long maxSoFar = arr[0]; // 全局最大值
for (size_t i = 1; i < arr.size(); ++i) {
// 要么接上前面,要么从当前重新开始
maxEndingHere = max(arr[i], maxEndingHere + arr[i]);
maxSoFar = max(maxSoFar, maxEndingHere);
}
return maxSoFar;
}
int main() {
int size;
cin >> size;
vector<vector<long long>> mat(size, vector<long long>(size));
// 输入矩阵
for (int i = 0; i < size; ++i)
for (int j = 0; j < size; ++j)
cin >> mat[i][j];
long long globalMax = LLONG_MIN; // 全局答案(必须初始化为极小值!)
// 枚举所有可能的上下边界
for (int top = 0; top < size; ++top) {
vector<long long> colSum(size, 0); // 动态维护从 top 到 bottom 的列和
for (int bottom = top; bottom < size; ++bottom) {
// 把第 bottom 行加到列和中(按列累加)
for (int j = 0; j < size; ++j) {
colSum[j] += mat[bottom][j];
}
// 对当前压缩后的一维数组跑 Kadane
long long currentMax = kadane(colSum);
globalMax = max(globalMax, currentMax);
}
}
cout << globalMax << endl;
return 0;
}
Kadane 算法就像炒股一样做决策!
Kadane 算法的本质是每天决定要不要“重开”:
🔑 核心口诀:
每天算两笔账:
① 带上昨天一起干能赚多少?(current + a[i])
② 今天单干能赚多少?(a[i])
选大的那个!同时记下历史最高赚了多少。
📈 举个栗子:
数组:[2, -4, 3, -1, 2, -4, 3]
| 步骤 | 当前选择 | 说明 |
|---|---|---|
| 第1天 | 2 | 只能选它 |
| 第2天 | -2 | 2 + (-4) 比 -4 好 |
| 第3天 | 3 | 重开!比 -2 + 3 = 1 更好 |
| 第4天 | 2 | 3 + (-1) 继续干 |
| 第5天 | 4 | 2 + 2 → 新高! |
| ... | ... | 最终答案 = 4 |
[?]常见疑问
Q:如果全是负数(如 [-5, -2, -8])怎么办?
A:那就选亏得最少的那天!
- 第1天:-5 → 最高 = -5
- 第2天:
max(-7, -2) = -2→ 最高 = -2 - 第3天:
max(-10, -8) = -8→ 最高仍为 -2 【答案对的】
Q:为什么不能初始化 globalMax = 0?
A:因为题目要求必须选至少一个元素!如果全为负,答案不可能是 0(那等于没选),所以必须用 LLONG_MIN 或 a[0] 初始化。
本题思路:降维打击🥵🥵🥵!
原问题是二维最大子矩阵和,但我们可以通过以下三步解决:
-
枚举上下边界(
top和bottom)
→ 固定子矩阵的行范围 -
列压缩
→ 把top到bottom行的每一列加起来,得到一维数组colSum -
Kadane 找左右边界
→ 在colSum上跑 Kadane,自动找出最优的左右列!
这样,所有可能的子矩阵都被覆盖了,而且时间复杂度仅为 ,完全满足 的要求。
为什么这样能覆盖所有情况?
- 任何一个子矩阵都有唯一的上下边界
(top, bottom) - 对每个
(top, bottom),Kadane 会自动找出最优左右边界 - 所以无需显式枚举左右边界,Kadane 帮你在线性时间内搞定!
总结
- Kadane:一维最大子段和()
- 枚举 + 压缩:把二维问题拆成多个一维问题( 次)
- 总复杂度:,最稳的一集你知道吗?
这道题完美展示了 “降维 + 经典算法” 的威力!希望我的分享对你有帮助
💬 P.S. 如果你觉得这个思路有用,不妨点个赞!也欢迎在评论区讨论其他解法~
主播我觉得我过不了校队选拔
全部评论 1
其实我有原稿:
先给代码在逐个解释:
#include<bits/stdc++.h> using namespace std; long long kadane(const vector<long long>& arr) { long long maxSoFar = arr[0]; long long maxEndingHere = arr[0]; for (size_t i = 1; i < arr.size(); ++i) { maxEndingHere = max(arr[i], maxEndingHere + arr[i]); maxSoFar = max(maxSoFar, maxEndingHere); } return maxSoFar; } long long globalMax = LLONG_MIN; int main() { int size; cin >> size; vector<vector<long long>> Array(size, vector<long long>(size)); for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { cin >> Array[i][j]; } } for (int top = 0; top < size; top++) { vector<long long> colsum(size, 0); for (int bottom = top; bottom < size; bottom++) { for (int ip = 0; ip < size; ip++) { colsum[ip] += Array[bottom][ip]; } long long currentValue = kadane(colsum); globalMax = max(globalMax, currentValue); } } cout << globalMax; }这里首先讲一下kadane算法是怎样的:
kadane算法就像是去让你投股票,你会实时决定是否要让它继续在这条股上继续随着股而波动,或者立即收杆重投。
核心口诀就像入xia:
每天算两笔账:
① 带上昨天一起干能赚多少?
② 今天单干能赚多少?
一定选大的那个!
同时记下历史最高赚了多少。”
比如说你有一个数组长的是这样:
2 -4 3 -1 2 -4 3
然后就是这样的过程:
第一次:只有2得选,现在最高是2
第二次:要是这两天的和一块就是2-4=-2,而要是直接从-4重来可是亏大了的,这里就选择-2,不比之前高
第三次:如果我再把之前的在加过来,算得3-2=-1,但是这次单干就是直接从3开始了,比-1和之前都要高,直接重投好吧,所以这里选3,这里最高是3
第四次:我今天把上次的3和今天的变化加过来就是3-1=2,要是重投就是-1了,所以我还是选择继续现在的吧,选2,不比之前高.
· · · · · ·
一直到最后,你就会找到一个最大值,是4了你肯定know的
有的人就会说:如果全是持续(比如 [-5, -2, -8]),怎么办?那就选亏得最少的那次嘛!
第1天:-5接着设当前=-5,然后最高=-5
第2天:max(-5-2, -2) = -2 然后再判断,最高 = max(-5, -2) = -2
第3天:max(-2-8, -8) = -8 ,最高还是 -2
答案就该是-2
然后又有的人要问:为什么不能初始化 历史最高为0?
因为题目要求必须选至少连续是一个的,如果全负数,答案就不可能是 0,那就是很错的。* **本题分析** 然后这里这题的思路是通过枚举和压缩来使上下边界的每种情况都被考虑到,接着压缩(对于每一对 (top, bottom),计算每一列在这些行上的总和,形成一个一维数组。这个步骤将原本的二维问题简化为一维问题,便于后续处理。),降维后用**Kadane**来做到寻找左右边界的作用(本质其实这个就是在每次横着切片在压成一维时可以在片段中找到最大值) 由此我们可以先写一个Kadane函数方便后续使用: ```cpp long long kadane(const vector<long long>& arr) { long long maxSoFar = arr[0]; long long maxE2025-12-12 来自 广东
0


有帮助,赞一个