C++---Interval DP---Chessboard Segmentation (Daily One Algorithm 2023.5.2)

Note: It is recommended to understand some knowledge
about "matrix/two-dimensional prefix sum" first.

Title:
Divide an 8×8 chessboard as follows: Cut the original chessboard into a rectangular chessboard and make the remaining part also a rectangle, and then continue to divide the remaining part in this way. After cutting (n−1) times, Together with the last remaining rectangular chessboard, there are n rectangular chessboards in total. (Each cut can only be made along the edge of the checkerboard grid)
Please add a picture description

Each frame on the original chessboard has a score, and the total score of a rectangular chessboard is the sum of the points of each frame it contains.
Now it is necessary to divide the chessboard into n rectangular chessboards according to the above rules, and minimize the mean square error of the total score of each rectangular chessboard.

Mean square error Please add a picture description, where the mean value Please add a picture description, xi is the total score of the i-th rectangular chessboard.

Please program to find the minimum value of the mean square error for the given chessboard and n.

Input Format
Line 1 is an integer n.
Each line from the 2nd to the 9th line contains 8 non-negative integers less than 100, representing the score of the corresponding grid on the chessboard. Each row is separated by a space between two adjacent numbers.

Output Format
Output the minimum mean square error value (rounded to three decimal places).

Data range
1<n<15

输入:
3
1 1 1 1 1 1 1 3
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 0
1 1 1 1 1 1 0 3
输出:
1.633
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 15, M = 9, INF = 1e9;
int n, m = 8;                   //n为最多分割的次数,m固定为棋盘的长宽8*8
int s[M][M];                    //s数组是棋盘的二维前缀和,用来快捷的求出一个区域内的值的总和
double f[M][M][M][M][N];        //f[x1][y1][x2][y2][k]
double X;                       //大写X表示均方差公式中的X拔

double get(int x1, int y1, int x2, int y2) {
    
            //得到(x1, y1)为左上点,(x2, y2)为右下点的区间内的值
    double sum = (s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1]) - X;
    return sum*sum / n;
}

double dp(int x1, int y1, int x2, int y2, int k) {
    
      //区间dp
    double &v = f[x1][y1][x2][y2][k];
    if (v >= 0) return v;                       //记忆化搜索,如果当前区间已被计算,直接返回值即可
    if (k == 1) return get(x1, y1, x2, y2);     //如果当前k为1说明是最后一刀,那么就直接返回(x1, y1)(x2, y2)这个区间内的值即可

    //v设为正无穷,因为要取min
    v = INF;
    for (int i = x1; i<x2; i++) {
    
       //枚举x1到x2之间的每一个分割线
        v = min(v, dp(x1, y1, i, y2, k-1) + get(i+1, y1, x2, y2));
        v = min(v, dp(i+1, y1, x2, y2, k-1) + get(x1, y1, i, y2));
    }
    for (int i = y1; i<y2; i++) {
    
       //枚举y1到y2之间的每一个分割线
        v = min(v, dp(x1, y1, x2, i, k-1) + get(x1, i+1, x2, y2));
        v = min(v, dp(x1, i+1, x2, y2, k-1) + get(x1, y1, x2, i));
    }
    return v;
}

int main() {
    
    
    //读入,处理二维前缀和
    cin >> n;
    for (int i = 1; i<=m; i++) {
    
    
        for (int j = 1; j<=m; j++) {
    
    
            cin >> s[i][j];
            s[i][j] = s[i][j] + s[i-1][j] + s[i][j-1] - s[i-1][j-1];
        }
    }

    //f初始化为负数
    memset(f, -1, sizeof f);
    X = (double)s[m][m] / n;
    
    //输出小数点3位,并把算出的最小值开方
    printf("%.3lf", sqrt(dp(1, 1, 8, 8, n)));
    return 0;
}

Idea:
The idea is very simple. In fact, it is to enumerate all the division methods of the chessboard.
Specifically, each time the chessboard is cut horizontally or vertically, the chessboard is divided into two rectangular sub-chessboards.
After one division, we can choose two sub-chessboards. One of the chessboards continues to recursively operate
until the number of divisions k is exhausted or it can no longer be cut.

It is still the classic y-style dp analysis method
1. State representation
f[x1][y1][x2][y2][k] :
take (x1, y1) as the upper left corner, (x2, y2) as the area of ​​the lower right corner, divide kall the schemes, and
the attribute is Min (minimum mean square error).

2. State calculation
Enumerate the segmentation possibilities of all currently selected intervals:

1. Cross-cutting, y1to y2all the dividing lines between k(cutting the y-axis)
Upper interval value: top = value(x1, y1, x2, k)
Lower interval value: bot = value(x1, k+1, x2, y2)

2. Vertical cut, x1to x2all the dividing lines between k(cut the x-axis)
Left interval value: left = value(x1, y1, k, y2)
Right interval value: right = value(k+1, y1, x2, y2)

Note that the crosscutting and vertical cutting are juxtaposed rather than nested, because the crosscutting cannot be vertically cut, and the vertical cutting cannot be crosscutted, and only one can be used. Min(top, bot, left, right) is The minimum mean square error of the current interval.

If it is helpful, please give a free like~ Someone watching is the motivation to support me to write down!

Disclaimer:
The source of the algorithm idea is Mr. Y. For details, please refer to https://www.acwing.com/
This article is only used for learning records and exchanges

Guess you like

Origin blog.csdn.net/SRestia/article/details/130463719