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
题意: 求方差最小.
解题思路: 黑书上的例题.
1. 方差公式变形: 先取平方 δ^2 = (1/n)(∑Xi^2 + n*(x)^2 - 2 * x * ∑(Xi)^2)
= (1/n) * ∑(Xi)^2 - x^2 (x: 平均值).
2. 公式变形后, 显然, 平均值是常量. 只要每个矩形的总分平均和最小即可.
3. 要确定矩形, 左上角(x1,y1), 右下角(x2,y2)就可以确定出矩形.
问题分析:
(1). 设: s[x1][y1][x2][y2]表示矩阵的分数总和.
dp[x1][y1][x2][y2][k]表示s[x1][y1][x2][y2]切割了k次后得到k+1块矩形的
总分平方和和最小值.
竖向: dp[x1][y1][x2][y2][k]=
min( dp[x1][y1][a][y2][k-1] + s[a+1][y1][x2][y2]^2,
dp[a+1][y1][x2][y2][k-1] + s[x1][y1][a][y2]^2 );
横向: dp[x1][y1][x2][y2][k]=
min( dp[x1][y1][x2][a][k-1] + s[x1][a+1][x2][y2]^2 ,
dp[x1][a+1][x2][y2][k-1] + s[x1][y1][x2][a]^2 );
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
const int INF = (1<<30);
int dp[9][9][9][9][15];
int s[9][9][9][9];
int d[9][9];
int n;
inline int min(int a,int b)
{
return a < b ? a : b;
}
void init()
{
int x1, y1, x2, y2;
int i, j;
int sum;
for(x1 = 1; x1 <= 8; ++x1)
{
for(y1 = 1; y1 <= 8; ++y1)
{
for(x2 = 1; x2 <= 8; ++x2)
{
for(y2 = 1; y2 <= 8; ++y2)
{
sum = 0;
for(i = x1; i <= x2; ++i)
{
for(j = y1; j <= y2; ++j)
{
sum += d[i][j];
}
}
s[x1][y1][x2][y2] = sum;
dp[x1][y1][x2][y2][1] = sum*sum;
}
}
}
}
}
int main()
{
int i, j;
//freopen("input.txt","r",stdin);
while(scanf("%d",&n) != EOF)
{
memset(dp,0,sizeof(dp));
memset(s,0,sizeof(s));
memset(d,0,sizeof(d));
double p = 0;
int x1, y1, x2, y2;
int a, t, temp;
for(i = 1; i <= 8; ++i)
{
for(j = 1; j <= 8; ++j)
{
scanf("%d\n",&d[i][j]);
p += d[i][j];
}
}
p /= n;
init();
for(int k = 2; k <= n; ++k)
{
for(x1 = 1; x1 <= 8; ++x1)
{
for(y1 = 1; y1 <= 8; ++y1)
{
for(x2 = 1; x2 <= 8; ++x2)
{
for(y2 = 1; y2 <= 8; ++y2)
{
temp = INF;
//横向
for(a = x1; a < x2; ++a)
{
t = min( dp[x1][y1][a][y2][k-1] + s[a+1][y1][x2][y2]*s[a+1][y1][x2][y2],
dp[a+1][y1][x2][y2][k-1] + s[x1][y1][a][y2]*s[x1][y1][a][y2] );
if(temp > t)
temp = t;
}
//竖向
for(a = y1; a < y2; ++a)
{
t = min( dp[x1][y1][x2][a][k-1] + s[x1][a+1][x2][y2]*s[x1][a+1][x2][y2],
dp[x1][a+1][x2][y2][k-1] + s[x1][y1][x2][a]*s[x1][y1][x2][a]);
if(temp > t)
temp = t;
}
dp[x1][y1][x2][y2][k] = temp;
}
}
}
}
}
printf("%.3f\n",sqrt( (double)dp[1][1][8][8][n] / (double)n - p*p) );
}
return 0;
}