WOJ-12-Think And Count题解

看了网上很多题解,发现有些难懂,自己写一个,代码应该看起来也比较容易懂一些

题目

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
#include

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;
}

你可能感兴趣的:(算法,c++,动态规划)