题面
分析:
看到题目,我们首先要 将均方差 的公式化简:
σ
=
∑
i
=
1
n
(
x
i
−
x
)
2
n
(
x
=
∑
i
=
1
n
x
i
n
,即平均数
)
\sigma = \sqrt{\frac{\sum_{i = 1}^{n}(x_i - x)^2}{n}} (x = \frac{\sum_{i = 1}^n x_i}{n},即平均数)
σ=n∑i=1n(xi−x)2(x=n∑i=1nxi,即平均数)
σ 2 = ∑ i = 1 n ( x i − x ) 2 n \sigma^2 = \frac{\sum_{i = 1}^{n}(x_i - x)^2}{n} σ2=n∑i=1n(xi−x)2
n ∗ σ 2 = ∑ i = 1 n ( x i − x ) 2 n * \sigma^2 = \sum_{i = 1}^{n}(x_i - x)^2 n∗σ2=∑i=1n(xi−x)2
n ∗ σ 2 = ∑ i = 1 n ( x i 2 − 2 ∗ x i ∗ x + x 2 ) n * \sigma^2 = \sum_{i = 1}^{n}(x_i^2 - 2 * x_i * x + x^2) n∗σ2=∑i=1n(xi2−2∗xi∗x+x2)
n ∗ σ 2 = ∑ i = 1 n x i 2 − 2 ∗ x ∗ ∑ i = 1 n x i + n ∗ x 2 n * \sigma^2 = \sum_{i = 1}^{n}x_i^2 - 2 *x * \sum_{i = 1}^{n}x_i + n * x^2 n∗σ2=∑i=1nxi2−2∗x∗∑i=1nxi+n∗x2
将 x = ∑ i = 1 n x i n x = \frac{\sum_{i = 1}^n x_i}{n} x=n∑i=1nxi 变形得到 n ∗ x = ∑ i = 1 n x i n *x = \sum_{i = 1}^{n} x_i n∗x=∑i=1nxi并带入上式:
n ∗ σ 2 = ∑ i = 1 n x i 2 − 2 ∗ n ∗ x 2 + n ∗ x 2 n * \sigma^2 = \sum_{i = 1}^{n}x_i^2 - 2 * n * x^2 +n * x^2 n∗σ2=∑i=1nxi2−2∗n∗x2+n∗x2
n ∗ σ 2 = ∑ i = 1 n x i 2 − n ∗ x 2 n * \sigma^2 = \sum_{i = 1}^{n}x_i^2 - n * x^2 n∗σ2=∑i=1nxi2−n∗x2
因为无论怎样划分 ∑ i = 1 n x i \sum_{i = 1}^{n}x_i ∑i=1nxi 都是固定的,即整块棋盘的总分值,又因为 n n n 给定且大于 0 0 0,所以 x x x 也是固定的。那么 n ∗ x 2 n * x^2 n∗x2 也是定值。 现在要使得 σ \sigma σ 最小,则 ∑ i = 1 n x i 2 − n ∗ x 2 \sum_{i = 1}^{n}x_i^2 - n * x^2 ∑i=1nxi2−n∗x2 就要最小,则 ∑ i = 1 n x i 2 \sum_{i = 1}^{n}x_i ^2 ∑i=1nxi2 就要最小。
现在问题转化为了 将原棋盘按规则分成 n n n 块且每一块分值的平方最小。 我们考虑到 大矩阵是由小矩阵拼接而成。例如我要将一个大矩阵分成 k k k 块,那么其实相当于把它分成两小块,其中一块是已经确定的,另一小块要再被分成 k − 1 k - 1 k−1 块。根据 DP最优子结构的性质我们知道: k k k 一定是这个动态规划的阶段。而且本题与 区间DP 非常相似,可看作 二维区间DP 来做,区间长度对应的就是 k k k。
因为对于一个大矩阵而言,将他分为两个矩阵的方式有两种 (竖着切或横着切),而在切完后要选择一个小矩阵作为剩余矩阵接着切,有两种选择小矩阵的方法。于是我们要分情况进行比较,取最优值。
对于每个矩阵的分值而言,我们可以使用二维前缀和进行预处理。
CODE:
#include <bits/stdc++.h>//设dp[x1][y1][x2][y2][k]表示将左上角为(x1, y1), 右下角为(x2, y2) 的矩阵划分成k个小矩阵的最小x_i^2的累和值
using namespace std;
const int M = 10, N = 20;
int n;
double dp[M][M][M][M][N], mp[M][M], sum[M][M], INF = 1e18;
void Get_sum(){//求二维前缀和
for(int i = 1; i <= 8; i++){
for(int j = 1; j <= 8; j++){
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + mp[i][j];
}
}
}
void pre_work(){//因为要求最小值,所以给DP数组附极大值
for(int k = 1; k <= n; k++){
for(int x1 = 1; x1 <= 8; x1++){
for(int y1 = 1; y1 <= 8; y1++){
for(int x2 = 1; x2 <=8; x2++){
for(int y2 = 1; y2 <= 8; y2++){
dp[x1][y1][x2][y2][k] = INF;
}
}
}
}
}
}
double Get(int x1, int y1, int x2, int y2){//求左上角为(x1,x2)右下角为(x2,y2)的矩阵分值
double x = sum[x2][y2] - sum[x1 - 1][y2] - sum[x2][y1 - 1] + sum[x1 - 1][y1 - 1];
return x * x;//平方处理
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= 8; i++){
for(int j = 1; j <= 8; j++){
scanf("%lf", &mp[i][j]);
}
}
Get_sum();
pre_work();
double average = sum[8][8] / n;//平均数
for(int k = 1; k <= n; k++){//阶段
for(int x1 = 1; x1 <= 8; x1++){
for(int y1 = 1; y1 <= 8; y1++){
for(int x2 = x1; x2 <=8; x2++){
for(int y2 = y1; y2 <= 8; y2++){
double &p = dp[x1][y1][x2][y2][k];
if(k == 1) p = Get(x1, y1, x2, y2);
else{
for(int row = x1 + 1; row <= x2; row++){//横截 //决策
p = min(p, dp[x1][y1][row - 1][y2][k - 1] + Get(row, y1, x2, y2));//要上
p = min(p, dp[row][y1][x2][y2][k - 1] + Get(x1, y1, row - 1, y2));//要下
}
for(int line = y1 + 1; line <= y2; line++){//竖截 决策
p = min(p, dp[x1][y1][x2][line - 1][k - 1] + Get(x1, line, x2, y2));//要左
p = min(p, dp[x1][line][x2][y2][k - 1] + Get(x1, y1, x2, line - 1));//要右
}
}
}
}
}
}
}
printf("%.3lf", sqrt(1.0 * (dp[1][1][8][8][n] - n * average * average) / n));//将dp[1][1][8][8][n]带回式子并求出答案
return 0;
}