|
|
#include <bits/stdc++.h>
|
|
|
|
|
|
using namespace std;
|
|
|
const int N = 10; // 8*8个格子,我们从下标1开始放入,需要用到下标8,开10个。
|
|
|
const int INF = 0x3f3f3f3f;
|
|
|
int n;
|
|
|
int m = 8;
|
|
|
int s[N][N]; // 二维前缀和
|
|
|
double f[N][N][N][N][N]; // DP结果数组
|
|
|
|
|
|
// 二维前缀和应用
|
|
|
int get_sum(int x1, int y1, int x2, int y2) {
|
|
|
return s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1];
|
|
|
}
|
|
|
|
|
|
// 均方差公式[模拟了题目给的公式] 注意这里没有开根号,最后开一次根号就行
|
|
|
double get(int x1, int y1, int x2, int y2) {
|
|
|
// 利用二维前缀和的结果,计算出平均值,注意要使用double的类型转换,防止丢失精度
|
|
|
double X = (double)s[m][m] / n; // 平均数
|
|
|
double sum = get_sum(x1, y1, x2, y2) - X;
|
|
|
return sum * sum;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 功能:记忆化搜索
|
|
|
* @param x1 左上角x坐标
|
|
|
* @param y1 左上角y坐标
|
|
|
* @param x2 右下角x坐标
|
|
|
* @param y2 右下角y坐标
|
|
|
* @param k 剩余的刀数
|
|
|
* @return 根据公式计算出的最小值
|
|
|
*/
|
|
|
double dfs(int x1, int y1, int x2, int y2, int k) {
|
|
|
double &v = f[x1][y1][x2][y2][k];
|
|
|
if (v >= 0) return v; // 计算过了,就直接返回,不再重复计算,v是一个double,不能用~判断是不是-1
|
|
|
if (k == 0) return v = get(x1, y1, x2, y2); // 如果k=0,表示刀都用完了,最终这一块可以计算出来了
|
|
|
|
|
|
// v:-1 表示没有计算过 v:INF 马上要进行计算,先设置最大
|
|
|
v = INF;
|
|
|
|
|
|
// 每次枚举的是i和i + 1之间分界线
|
|
|
// 选择横着切,从x1行开始(这个是固定的),到i行(需要枚举的)结束
|
|
|
for (int i = x1; i < x2; i++) {
|
|
|
// 放弃上半部分,选择下半部分
|
|
|
v = min(v, get(x1, y1, i, y2) + dfs(i + 1, y1, x2, y2, k - 1));
|
|
|
// 放弃下半部分,选择上半部分
|
|
|
v = min(v, get(i + 1, y1, x2, y2) + dfs(x1, y1, i, y2, k - 1));
|
|
|
}
|
|
|
// 选择纵着切
|
|
|
for (int i = y1; i < y2; i++) {
|
|
|
// 放弃左半部分,选择右半部分
|
|
|
v = min(v, get(x1, y1, x2, i) + dfs(x1, i + 1, x2, y2, k - 1));
|
|
|
// 放弃右半部分,选择左半部分
|
|
|
v = min(v, get(x1, i + 1, x2, y2) + dfs(x1, y1, x2, i, k - 1));
|
|
|
}
|
|
|
// 返回打擂台的最小值
|
|
|
return v;
|
|
|
}
|
|
|
|
|
|
int main() {
|
|
|
scanf("%d", &n); // 切成n块
|
|
|
|
|
|
for (int i = 1; i <= m; i++)
|
|
|
for (int j = 1; j <= m; j++) {
|
|
|
// 原数组不用保存,直接用一个二维前缀和数组s即可
|
|
|
scanf("%d", &s[i][j]);
|
|
|
// 二维前缀和
|
|
|
s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
|
|
|
}
|
|
|
|
|
|
// 将DP数组初始化为负无穷,计算过的>=0 (因为均方差可能为0),未计算过的为-1,方便获取哪个位置是否计算过
|
|
|
// 问题:为什么不用memset(f, -1, sizeof f)?
|
|
|
// 答:一般double类型的数组初始化,喜欢用多重循环,不喜欢用memset,可能会有坑,当然,本题用memset也正确
|
|
|
for (int k = 0; k < 15; k++)
|
|
|
for (int x1 = 1; x1 <= m; x1++)
|
|
|
for (int y1 = 1; y1 <= m; y1++)
|
|
|
for (int x2 = 1; x2 <= m; x2++)
|
|
|
for (int y2 = 1; y2 <= m; y2++)
|
|
|
f[x1][y1][x2][y2][k] = -1;
|
|
|
|
|
|
// 记忆化搜索:因为最后需要切出n块矩形棋盘,其实就是需要切n-1刀,开始dfs模拟
|
|
|
printf("%.3lf\n", sqrt(dfs(1, 1, 8, 8, n - 1) / n));
|
|
|
return 0;
|
|
|
} |