Description
将一个8*8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割,这样割了(n-1)次后,连同最后剩下的矩形棋盘共有n块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行)
原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。
均方差 ,其中平均值 ,x i为第i块矩形棋盘的总分。
请编程对给出的棋盘及n,求出O'的最小值。
原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。
均方差 ,其中平均值 ,x i为第i块矩形棋盘的总分。
请编程对给出的棋盘及n,求出O'的最小值。
Input
第1行为一个整数n(1 < n < 15)。
第2行至第9行每行为8个小于100的非负整数,表示棋盘上相应格子的分值。每行相邻两数之间用一个空格分隔。
第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;
}