看了网上很多题解,发现有些难懂,自己写一个,代码应该看起来也比较容易懂一些
题目
You are given a chessboard made up of N squares by M squares. Some of the squares are colored black, and the others are colored white. Please
write a program to calculate the number of rectangles which are completely made up of white squares.
输入格式
There are multiple test cases. Each test case begins with two integer N,M (1 <= N , M<= 2000), the board size. The following N lines, each with M
characters, have only two valid character values:
b - representing a black square;
w - representing a white square.
Process to the end of file.
输出格式
For each test case in the input, output the number of white rectangles a line.
样例输入
2 3
bbb
www
2 2
bw
wb
样例输出
6
2
启发式想法
这题不会做,在网上搜矩形个数的计算方法,找到了这样一个计算公式:
// 由w*l个小方块组成的矩形,共有(w+1)*(l+1)个可以选为矩形顶点的点
// 选第一个点:有(w+1)*(l+1)中可能,再选矩形的另一个对角顶点有 w*l 种可能,但是CA AC DB BD都表示同一个矩形,所以再除以4
int rectangleArea(int w, int l) {
return (w+1) * (l+1) * w * l / 4;
}
再去看别人的题解,了解了一些滚动数组的思想,简单来说就是一次读取一行的棋盘状态,进行处理,相当于固定了矩形的右下角,根据目前这一列上的矩形高度、以及能达到的宽度,来判断小矩形能组成的矩形个数。
// 右下角固定,存在多少个矩形种类(即左上角有几种选择)
long rightDownFixRect(int width, int height) {
return width * height;
}
当前列上矩形的高度可以根据上一行的高度决定,所以我们只需要确定新的 最大的矩形宽度是多少就可以了。
运用动态规划的思想,定义一个数组sum[M],sum[i]保存当前行的、到第i列为止,可以组成的矩形数量。
如果第i列的高度比左边的所有矩形都要高,那么就可以使用动态规划的思想,新的以这列的底部小方块为右下角的最大矩形就是:高度为当前列高度、宽度为1的矩形。对于这列与左边矮一些的列组成的矩形,已经被之前的sum[i-1]计算过了,直接加上就好了。
最后一个问题,就是确定对于第i列来说,之前的那一列比这一列矮。判断逻辑见代码。
#include<iostream>
#include<vector>
using namespace std;
// 右下角固定,存在多少个矩形种类(即左上角有几种选择)
long rightDownFixRect(int width, int height) {
return width * height;
}
int main() {
int N, M;
while (cin >> N >> M) {
long long res = 0;
vector<int> height = vector<int>(M, 0); // 当前列的宽度为1的矩形高度
vector<int> width = vector<int>(M, 0); // width[i]表示能以height[i]为高的矩形的最大宽度
vector<int> sum = vector<int>(M, 0); // sum[i]表示当前行x以列i为右下角的矩形总数
char input;
for (int i = 0; i < N; ++i) {
for (int j = 0; j < M; ++j) {
cin >> input;
if (input == 'w') {
height[j]++;
} else {
height[j] = 0;
}
}
sum[0] = 0;
for (int j = 0; j < M; ++j) {
if (j == 0) {
sum[j] += rightDownFixRect(1, height[j]);
} else if (height[j-1] <= height[j]) {
sum[j] = rightDownFixRect(1, height[j]) + sum[j-1]; // 仅由第j列组成的矩形个数 + j-1列矩形拓宽一列的矩形个数
width[j] = 1;
} else if (height[j-1] > height[j]) {
width[j] = width[j-1] + 1; // 比height[j]为高的矩形可以拓宽
int leftRecIndex = j - width[j]; // 左边比height[j]还矮的矩形列下标
// 查找可以以height[j]为高延伸到的最远列下标
while (height[leftRecIndex] >= height[j] && leftRecIndex > 0) {
leftRecIndex = leftRecIndex - width[leftRecIndex];
}
// j列左边的矩形都比自己高
if (height[leftRecIndex] >= height[j]) {
width[j] = j;
sum[j] = rightDownFixRect(j+1, height[j]);
} else {
// 找到了一个比当前列要矮的
width[j] = j - leftRecIndex;
// 以自己为高 以最矮的矩形为高
sum[j] = rightDownFixRect(width[j], height[j]) + sum[leftRecIndex];
}
}
res += sum[j];
}
// cout << "Height: ";
// std::for_each(height.begin(), height.end(), [](int val){ cout << val << " "; }); cout << endl << "Width: ";
// std::for_each(width.begin(), width.end(), [](int val){ cout << val << " "; }); cout << endl << "Sum: ";
// std::for_each(sum.begin(), sum.end(), [](int val){ cout << val << " "; }); cout << endl << endl;
}
cout << res << endl;
}
return 0;
}