POJ 1191 棋盘分割(DP)

4 篇文章 0 订阅
4 篇文章 0 订阅

Description

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

原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。 
均方差 ,其中平均值 ,x i为第i块矩形棋盘的总分。 
请编程对给出的棋盘及n,求出O'的最小值。 

Input

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

Output

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

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

分析:转化为求x_i平方和最小,用dp解决
写的时候出了几个错误,是以后需要注意的:
1)dp主循环无法结束,开始怀疑是复杂度算错了。但其实这个循环复杂度非常低,于是打印出循环变量的值,但是并没有发现数值范围异常(溢出之类)。最后发现是一个下标里面的k-1写成了k=1,导致循环无法结束。
总结:如果出现程序无法跳出某个循环的情况,并且不是复杂度问题,应该检查一下循环控制变量是否被意外赋值。有时变量重名也会导致类似问题。
2)做dp时做成了切n刀,但是题目说了是切n块、n-1刀。在这个地方卡了很久。
总结:注意下标范围!!同时要想清楚自己写的每一行代码在做什么
3)在状态转移时有一个判断当前子棋盘是否可以被切k刀,如果不能当前状态赋值为inf。一开始这个地方判断的表达式写错了,多减了1,导致wa了三次,还以为是精度问题。
总结:类似上一条,想清楚表达式的含义

代码如下。
/*
	PROG: POJ1191
	PROB:
*/

#include <cstdio>
#include <cmath>
#include <cassert>
#include <algorithm>
using namespace std;

#define DEBUG 1
#define LOG(...) do { if (DEBUG) fprintf(stderr, __VA_ARGS__); } while(0)

#define INF 0x3f3f3f3f
#define MAXN 20
int dp[64][64][MAXN];
int s[64][64], g[64];
int a[8][8], b[8][8];
int enc[8][8];

void init(void) {
	for (int r = 0; r < 8; ++r)
		for (int c = 0; c < 8; ++c)
			enc[r][c] = 8*r+c;
}

long double sqr(long double x) {
	return x*x;
}

int main(void) {
	init();
	int n; scanf("%d", &n);
	for (int i = 0; i < 8; ++i)
		for (int j = 0; j < 8; ++j)
			scanf("%d", &(a[i][j]));
	for (int r = 0; r < 8; ++r)
		for (int c = 0; c < 8; ++c)
			b[r][c] = (!c)?a[r][c]:a[r][c]+b[r][c-1];
	for (int r = 0; r < 8; ++r)
		for (int c = 0; c < 8; ++c)
			g[enc[r][c]] = (!r)?b[r][c]:b[r][c]+g[enc[r-1][c]];
	for (int r1 = 0; r1 < 8; ++r1)
	for (int c1 = 0; c1 < 8; ++c1)
	for (int r2 = r1; r2 < 8; ++r2)
	for (int c2 = c1; c2 < 8; ++c2) {
		int i = enc[r1][c1], j = enc[r2][c2];
		s[i][j] = g[j];
		if (r1) s[i][j] -= g[enc[r1-1][c2]];
		if (c1) s[i][j] -= g[enc[r2][c1-1]];
		if (r1&&c1) s[i][j] += g[enc[r1-1][c1-1]];
		dp[i][j][0] = s[i][j]*s[i][j];
	}
	for (int k = 1; k <= n-1; ++k) {
		for (int r1 = 0; r1 < 8; ++r1)
		for (int c1 = 0; c1 < 8; ++c1)
		for (int r2 = r1; r2 < 8; ++r2)
		for (int c2 = c1; c2 < 8; ++c2) {
			int M = INF;
			if (r2-r1+c2-c1<k) {
				dp[enc[r1][c1]][enc[r2][c2]][k]=M;
				continue;
			}
			for (int r = r1; r < r2; ++r) {
				M = min(M, dp[enc[r1][c1]][enc[r][c2]][0]+dp[enc[r+1][c1]][enc[r2][c2]][k-1]);
				M = min(M, dp[enc[r1][c1]][enc[r][c2]][k-1]+dp[enc[r+1][c1]][enc[r2][c2]][0]);
			}
			for (int c = c1; c < c2; ++c) {
				M = min(M, dp[enc[r1][c1]][enc[r2][c]][0]+dp[enc[r1][c+1]][enc[r2][c2]][k-1]);
				M = min(M, dp[enc[r1][c1]][enc[r2][c]][k-1]+dp[enc[r1][c+1]][enc[r2][c2]][0]);
			}
			dp[enc[r1][c1]][enc[r2][c2]][k]=M;
			// LOG("dp[%d][%d][%d][%d][%d]=%d\n", r1,c1,r2,c2,k, M);
		}
	}
	float ans = (dp[enc[0][0]][enc[7][7]][n-1]*1.0f)/n;
	// LOG("%.3f\n", ans);
	float tmp = g[enc[7][7]]*1.0f/n;
	ans -= tmp*tmp;
	// LOG("%.3f\n", ans);
	ans = sqrt(ans);
	printf("%.3f\n", ans);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值