poj1191 棋盘分割(记忆化搜索)

题目传送门:http://poj.org/problem?id=1191

Description

将一个8*8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割,这样割了(n-1)次后,连同最后剩下的矩形棋盘共有n块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行)
在这里插入图片描述

原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。
均方差 σ = Σ i = 1 n ( x i − x ˉ ) 2 n σ=\sqrt \frac{Σ_{i=1}^n(x_i-\bar x)^2}{n} σ=nΣi=1n(xixˉ)2 ,其中平均值 x ˉ = Σ i = 1 n x i n \bar x=\frac{Σ_{i=1}^nx_i}{n} xˉ=nΣi=1nxi x i x_i xi为第i块矩形棋盘的总分。
请编程对给出的棋盘及n,求出 σ σ σ 的最小值。

Input

第1行为一个整数n(1 < n < 15)。
第2行至第9行每行为8个小于100的非负整数,表示棋盘上相应格子的分值。每行相邻两数之间用一个空格分隔。

Output

仅一个数,为 σ σ σ(四舍五入精确到小数点后三位)。

Sample Input

3
1 1 1 1 1 1 1 3
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 0
1 1 1 1 1 1 0 3

Sample Output

1.633

Source

Noi 99

1、先分析协方差的大小与什么有关

将协方差公式展开:

σ = Σ i = 1 n ( x i − x ˉ ) 2 n = Σ i = 1 n x i 2 − 2 Σ i = 1 n x i x ˉ + n x ˉ 2 n σ=\sqrt \frac{Σ_{i=1}^n(x_i-\bar x)^2}{n}=\sqrt \frac{Σ_{i=1}^nx_i^2-2Σ_{i=1}^nx_i\bar x+n\bar x^2}{n} σ=nΣi=1n(xixˉ)2 =nΣi=1nxi22Σi=1nxixˉ+nxˉ2

因为均值公式为:

x ˉ = Σ i = 1 n x i n \bar x=\frac{Σ_{i=1}^nx_i}{n} xˉ=nΣi=1nxi

将均值公式代入协方差公式,有:

σ = Σ i = 1 n x i 2 n − 2 x ˉ 2 + x ˉ 2 = Σ i = 1 n x i 2 n − x ˉ 2 σ=\sqrt{\frac{Σ_{i=1}^nx_i^2}{n} -2\bar x^2+\bar x^2}=\sqrt{\frac{Σ_{i=1}^nx_i^2}{n} -\bar x^2} σ=nΣi=1nxi22xˉ2+xˉ2 =nΣi=1nxi2xˉ2

因为均值 x ˉ \bar x xˉ 已经确定,所以协方差只和 Σ i = 1 n x i 2 Σ_{i=1}^nx_i^2 Σi=1nxi2 有关

2、关于记忆化搜索的状态表示

用5个变量表示状态:可以用左上角和右下角两个格子的坐标来表示整个区域,形如:(x1, y1, x2, y2),还需要使用一个变量 k 来表示需要切 k 刀

dfs(k, x1, y1, x2, y2):表示对当前区域切 k 刀后得到的最小平方和

sum(x1, y1, x2, y2):表示当前区域分数的平方

因为题目要求对当前区域进行切割后,要在切出的二个区域中选一个继续切,另一个区域舍弃

所以状态转移方程为(以横切为例):

dfs(k, x1, y1, x2, y2) = min(dfs(k - 1, x1, y1, i, y2) + sum(i + 1, y1, x2, y2), dfs(k - 1, i + 1, y1, x2, y2) + sum(x1, y1, i, y2))

纵切同理:

dfs(k, x1, y1, x2, y2) = min(dfs(k - 1, x1, y1, x2, i) + sum(x1, i + 1, x2, y2), dfs(k - 1, x1, i + 1, x2, y2) + sum(x1, y1, x2, i))

参考的题解链接:https://www.cnblogs.com/handsomecui/p/5207512.html

#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int N = 10; 

double arr[N][N]; // 前缀和数组 
double dp[16][N][N][N][N];

// 当前矩形分数的平方
double sum(int x1, int y1, int x2, int y2)
{
	// 二维前缀和 
	double x = arr[x2][y2] - arr[x1-1][y2] - arr[x2][y1-1] + arr[x1-1][y1-1];
	
	return x * x;
}
// k 表示还需要切几刀 
double dfs(int k, int x1, int y1, int x2, int y2)
{
	// 当前状态已经计算过 
	if (dp[k][x1][y1][x2][y2] >= 0)
		return dp[k][x1][y1][x2][y2];
	// 不需要再切 
	if (k == 0)
		return dp[k][x1][y1][x2][y2] = sum(x1, y1, x2, y2);
	
	double res = 0x3f3f3f3f;
	// 枚举横切的位置
	for (int i = x1; i < x2; i++) {
		res = min(res, dfs(k - 1, x1, y1, i, y2) + sum(i + 1, y1, x2, y2));
		res = min(res, dfs(k - 1, i + 1, y1, x2, y2) + sum(x1, y1, i, y2));
	} 
	// 枚举纵切的位置
	for (int i = y1; i < y2; i++) {
		res = min(res, dfs(k - 1, x1, y1, x2, i) + sum(x1, i + 1, x2, y2));
		res = min(res, dfs(k - 1, x1, i + 1, x2, y2) + sum(x1, y1, x2, i));
	} 
	
	return dp[k][x1][y1][x2][y2] = res;
}

int main(void)
{
	double n, x_; // 平均值 
	cin >> n;
	for (int i = 1; i <= 8; i++) {
		for (int j = 1; j <= 8; j++) {
			cin >> arr[i][j];
			arr[i][j] += arr[i - 1][j] + arr[i][j - 1] - arr[i - 1][j - 1];
		}
	}
	// 平均值 
	x_ = arr[8][8] / n;
	memset(dp, -1, sizeof dp);
	// 各个矩形的平方和 
	double res = dfs(n - 1, 1, 1, 8, 8);
	//cout << res << " " << x_ << endl;
	printf("%.3f\n", sqrt(res / n - x_ * x_));
	
	return 0;
} 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值