子矩陣問題
原作者飛翔的藍鯨
Description
給定一個矩形區域,每一個位置上都是1或0,求該矩陣中每一個位置上都是1的最大子矩形區域中的1的個數。
Input
輸入第一行爲測試用例個數。每一個用例有若干行,第一行爲矩陣行數n和列數m,下面的n行每一行是用空格隔開的0或1。
Output
輸出一個數值。
Sample Input 1
1
3 4
1 0 1 1
1 1 1 1
1 1 1 0
Sample Output 1
6
思路
思路看的論壇飛翔的藍鯨大神的,原文非常清晰。
本題中可以將矩陣轉化爲記錄從上往下將矩陣逐行作爲底層往上算每個位置連續爲1的個數
1 0 1 1 --- 1 0 1 1
1 1 1 1 --- 2 1 2 2
1 1 1 0 --- 3 2 3 0
這樣可以將轉化後的矩陣每一行看作一個直方圖
例如,對於第二行:3 2 3 0
代碼
#include <iostream>
#include <algorithm>
#include <stack>
using namespace std;
int a[99][99] = { 0 };
int main()
{
int t, n, m;
scanf("%d", &t);
for (int turn = 0; turn < t; turn++) {
scanf("%d%d", &n, &m);
fill(a[0], a[0] + n * m, 5);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
scanf("%d", &a[i][j]);
}
}
//從第二行開始將每一行中爲1的元素改爲從該位置往上連續1的個數
for (int i = 1; i < n; i++) {
for (int j = 0; j < m; j++) {
if (a[i][j] == 1 && a[i - 1][j] != 0) {//只向上算連續的1的個數
a[i][j] += a[i - 1][j];
}
}
}
int res = 0;//res爲最終結果
//遍歷矩陣每行算各行的符合條件的最大子矩陣,然後取各行結果maxTemp最大的爲最終結果res
for (int i = 0; i < n; i++) {
stack<int> s;
int j = 1, maxTemp = a[i][0];
s.push(0);
while (j<m || j==m && !s.empty()) {
//未將矩陣當前行所有元素入棧且(棧爲空或當前遍歷元素>=棧頂元素)時,
//將當前元素(矩陣該行的元素下標,如a[i][j],就將j入棧)入棧
if (j != m && (s.empty() || a[i][j] >= a[i][s.top()])) {
s.push(j);
j++;
}
//否則記錄下棧頂元素對應在矩陣中的值topNum,並彈出棧頂元素
//若當前棧不空,則當前彈出元素對應的最大子矩陣1的個數爲topNum*(j-s.top()-1)
//若當前棧空了,則說明彈出的元素爲最小元素,其對應最大子矩陣1個數就是topNum*j
//然後更新一下當前行的最大子矩陣1的個數maxTemp值
else {
int topNum = a[i][s.top()];
s.pop();
int currMax = !s.empty() ? topNum * (j - s.top() - 1) : topNum * j;
maxTemp = max(currMax, maxTemp);
}
}
res = max(maxTemp, res);
}
if (turn + 1 == t) {
printf("%d", res);
}
else {
printf("%d\n", res);
}
}
return 0;
}