You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

84 lines
3.5 KiB

2 years ago
#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;
}