/*
=========================================
Author:Bob Lee
2012.8.24
=========================================
核心思想:动态规划
题目的大意是有一个8*8的矩阵,每一个都有一个非负整数
然后要你切n-1下
最后就有了n块,每块的和记为Xi
然后给出一个公式Q=sqrt( sum(Xi-XAve)^2/n );
要你求出Q的最小值
首先是把Q平方,这样可以方便运算。
得Q^2 = sum(Xi^2)/n - XAve^2
XAve就是平均值,这个好求
难点就在前面的那一个求和,这个我们得用DP求
这是LRJ的黑书上关于动态规划的一道题目,核心思想已经讲了,我就原样COPY
By LRJ:
dp[k][x1][y1][x2][y2]:左上角坐标为(x1,y1),右下角坐标为(x2,y2)
的棋盘,设它把切割k次以后得到的k+1块矩形的总分平方和最小值.
s[x1][y1][x2][y2]:左上角坐标为(x1,y1),右下角坐标为(x2,y2)
的棋盘的总和的平方
dp[k][x1][y1][x2][y2] =
1)按横的划分: min(dp[k-1][x1][y1][f][y2]+s[f+1][y1][x2][y2]
, dp[k-1][f+1][y1][x2][y2]+s[x1][y1][f][y2]);
2)按竖的划分: min(dp[k-1][x1][y1][x2][f]+s[x1][f+1][x2][y2]
, dp[k-1][x1][f+1][x2][y2]+s[x1][y1][x2][f]);
我把dp 换成了d
这里还有一个地方就是处理s这个数组,这个还真的有点费脑筋(本人菜鸟)
我就是用的一个count-map数组保存的由1,1到x,y的和,这样就可一通过
double ans = count_map[i2][j2] - count_map[i2][j-1] - count_map[i-1][j2] + count_map[i-1][j-1];
这个公式搞定来,自己可以演算一下
还有一个精度问题,是用long double才AC的,用double就过不了,表示很纠结
*/
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int n;
long double d[16][9][9][9][9];
long double s[9][9][9][9];
double map[9][9];
int count_map[9][9];
long double sum;
long double ave;
const int m = 8;
const double INF = 9999999999;
double count(int i,int j,int i2,int j2)
{
double ans = count_map[i2][j2] - count_map[i2][j-1] - count_map[i-1][j2] + count_map[i-1][j-1];
return ans*ans;
}
void init() //初始化s这个数组
{
int i,j;
sum = 0;
memset(count_map,0,sizeof(count_map));
for(i=1;i<=8;i++)
{
for(j=1;j<=8;j++)
{
scanf("%lf",&map[i][j]);
sum+=map[i][j];
//count_map[i][j] 表示的是1,1到i,j的矩阵范围内的和
count_map[i][j] = count_map[i][j-1] +
count_map[i-1][j] -
count_map[i-1][j-1] +
map[i][j];
}
}
ave = sum*1.0/n;
int i2,j2;
for(i=1;i<=m;i++)
{
for(j=1;j<=m;j++)
{
for(i2=i;i2<=m;i2++)
{
for(j2 = j;j2<=m;j2++)
{
s[i][j][i2][j2] = count(i,j,i2,j2);
d[0][i][j][i2][j2] = s[i][j][i2][j2];
}
}
}
}
}
void dp()
{
int k,i,j,i2,j2;
for(k=1;k<n;k++)
{
for(i=1;i<=m;i++)
{
for(j=1;j<=m;j++)
{
for(i2=i;i2<=m;i2++)
{
for(j2 = j;j2<=m;j2++)
{
d[k][i][j][i2][j2] = INF;
//首先是横着切
int t;
for( t = i;t<i2;t++)
d[k][i][j][i2][j2] = min(d[k][i][j][i2][j2],
min(d[k-1][i][j][t][j2] + s[t+1][j][i2][j2],
s[i][j][t][j2] + d[k-1][t+1][j][i2][j2]));
//在就是竖着切
for( t = j;t<j2;t++)
d[k][i][j][i2][j2] = min(d[k][i][j][i2][j2],
min(d[k-1][i][j][i2][t] + s[i][t+1][i2][j2],
s[i][j][i2][t] + d[k-1][i][t+1][i2][j2]));
}
}
}
}
}
}
int main()
{
while(scanf("%d",&n) != EOF)
{
init();
dp();
long double ans = d[n-1][1][1][m][m]*1.0/n - ave*ave;
printf("%.3f\n",(double)sqrt(ans));
}
return 0;
}
题目地址:http://poj.org/problem?id=1191