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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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;
}