Files
python/TangDou/AcWing/Section/321_jyh_dfs.cpp
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

84 lines
3.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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