CSP - neighborhood mean (difference and prefix sum)

Question background

After Dun Dun learned digital image processing, he wanted to denoise a grayscale image on his hand. However, the image only has a lot of noise in the darker areas. If you denoise the whole image rashly, it will blur the original image while erasing the noise. Therefore, Dunton intends to use the neighborhood mean to determine whether a pixel is in a darker area , and then only perform noise reduction on pixels in the darker area .

Problem Description

The grayscale image to be processed has n pixels in length and width, and can be expressed as a matrix A of n×n size, where each element is an integer in the range of [0,L), representing the grayscale value of the pixel at the corresponding position .

For any element Aij (0≤i,j<n) in the matrix, its neighborhood is defined as the sum of several nearby elements:

Neighbor(i,j,r)={Axy|0≤x,y<n and |x−i|≤r and |y−j|≤r}

An additional parameter r is used here to indicate the specific range of elements near Aij. By definition, it is easy to know that Neighbor(i,j,r) has at most (2r+1)2 elements.

If the average value of all elements in the neighborhood element Aij is less than or equal to a given threshold t, we consider the pixel corresponding to the element to be in a darker area .

The image below shows two examples, where the darker areas of the image on the left are shown as black in the image on the right, and the remaining areas are shown as white.

Now given the neighborhood parameter r and the threshold t, try to count how many pixels are in the darker area in the input grayscale image .

input format

Input a total of n+1 lines.

The first line of input contains four positive integers n, L, r, and t separated by spaces, with the meanings described above.

The second to n+1th rows are input to matrix A.

Line i+2 (0≤i<n) contains n integers separated by spaces, in order Ai0, Ai1,⋯, Ai(n−1).

output format

Outputs an integer representing the total number of pixels in the darker region of the input grayscale image.

Problem-solving ideas:

When we get this question, let's first sort out what the question asks us to do. Simply think, we need to traverse the matrix first, and then for each element, first determine the range of its neighborhood (including the element itself), then traverse the neighborhood, sum all the elements inside, and then average, Finally, compare the average value with the threshold value, and if it is less than or equal to the threshold value, add 1 to the total number of pixels in the darker area.

We might as well write it in this way first. Sure enough, only 70 points were scored, indicating that the time limit was exceeded. This method is more violent, not optimized, and the time limit is also within my expectation.

Code for 70 points:

# include <iostream>
# include <algorithm>

using namespace std;

const int maxn = 1000;
int n, L, r, t;
int A[maxn][maxn];
int ans = 0;  //结果

int main()
{
    cin >> n >> L >> r >> t;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            scanf("%d", &A[i][j]);
        }
    }
    int a, x1, x2, y1, y2;
    //为避免比较时的误差,sum,aver,len要定义为浮点型
    float aver, sum, len;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            a = A[i][j];
            //x1,x2,y1,y2用于确定邻域范围
            x1 = max(i - r, 0);
            x2 = min(i + r, n - 1);
            y1 = max(j - r, 0);
            y2 = min(j + r, n - 1);
            sum = 0;
            len = 0;
            //求出邻域内所有元素的平均值
            for (int k = x1; k <= x2; k++) {
                for (int l = y1; l <= y2; l++) {
                    sum = sum + A[k][l];
                    len++;
                }
            }
            aver = sum / len;
            if (aver <= t) {
                ans++;
            }
        }
    }
    cout << ans << endl;
    return 0;
}

Now we start thinking about improving the algorithm. For the existing code, the step of traversing the matrix obviously cannot be optimized, so we naturally focus on the step of "summing the traversal of the neighborhood". The number of elements in the neighborhood can be directly calculated. If the sum of all elements in the neighborhood can be directly calculated without looping in this step, it can greatly save time. The question is can it be done? The answer is of course.

The idea of ​​difference and prefix sum is used here . We first create a difference matrix B. When initially inputting the matrix, for each input value Aij , we perform the following processing in advance: determine the neighborhood range of Aij , and for each value in the range, "neighborhood summation" ", Aij must be added (you are in my neighborhood, and of course I am also in your neighborhood), so we add Aij to all elements in the field in advance . Here, we use the idea of ​​difference, for each row of the neighborhood, we add Aij to the first element , and subtract Aij from the last element of the last element . After the input is over, we restore the difference matrix B (find the prefix sum). At this time, each value in the matrix B is the result of the sum of the neighborhoods of the original elements at this position. After that, it can be processed according to the original method.

100分的代码:

# include <iostream>
# include <algorithm>

using namespace std;

const int maxn = 1000;
int n, L, r, t;
int A[maxn][maxn];
float B[maxn][maxn] = { {0} };  //差分矩阵B
int ans = 0;   //结果

int main()
{
    cin >> n >> L >> r >> t;
    int a, x1, x2, y1, y2;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            scanf("%d", &A[i][j]);
            //x1,x2,y1,y2用于确定邻域范围
            x1 = max(i - r, 0);
            x2 = min(i + r, n - 1);
            y1 = max(j - r, 0);
            y2 = min(j + r, n - 1);
            //差分
            for (int k = y1; k <= y2; k++) {
                B[x1][k] += A[i][j];
                B[x2 + 1][k] -= A[i][j];
            }
        }
    }
    //还原(前缀和)
    for (int i = 0; i < n; i++) {
        for (int j = 1; j < n; j++) {
            B[j][i] = B[j][i] + B[j - 1][i];
        }
    }
    float aver, len;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            a = A[i][j];
            //x1,x2,y1,y2用于确定邻域范围
            x1 = max(i - r, 0);
            x2 = min(i + r, n - 1);
            y1 = max(j - r, 0);
            y2 = min(j + r, n - 1);
            //直接算出邻域范围
            len = (x2 - x1 + 1) * (y2 - y1 + 1);
            //求出邻域内所有元素的平均值
            aver = B[i][j] / len;
            if (aver <= t) {
                ans++;
            }
        }
    }
    cout << ans << endl;
    return 0;
}

总结:

这里以解题为主,差分和前缀和的思想,我就不做解释了(不知道的小伙伴可以去查一下相关的文章,有很多讲得都很细致)。CSP的第二题,一般都会有比较暴力直接的方法,但大多只能得70分;想要拿100分,就建议大家学习一下差分、DFS剪枝、动态规划等的算法思想,对满分通过第2题很有帮助。以上便是我对这道题的看法,很高兴与大家分享。

Guess you like

Origin blog.csdn.net/CXR_XC/article/details/129647147