http://poj.org/problem?id=1191
刘汝佳黑书上有证明,先化简了求解公式,最后直接关联到xi2(i=1,2,3…,n)。
看了好久,起初被五维数组吓到了。
后来想了一下,记录主要是为了避免TLE而已。
递归的思想,n==2的情况来想,就容易很多了^ ^
附上书上的截图
//pku poj 1191 棋盘分割
#include<iostream>
#include<math.h>
double d[10][10][10][10][20]; //记录状态
double s[10][10][10][10]; //记录每一块的和
double map[10][10];
double MY_min(double a,double b) //比较函数
{
return a<b ? a : b ;
}
double sum_area(int i1,int j1,int i2,int j2) //计算每一块的和
{
if(s[i1][j1][i2][j2]>=0) //这一步避免重复计算
return s[i1][j1][i2][j2];
int i,j;
double sum=0;
for(i=i1;i<=i2;i++)
{
for(j=j1;j<=j2;j++)
{
sum+=map[i][j];
}
}
s[i1][j1][i2][j2]=sum*sum; //预先平方
return s[i1][j1][i2][j2];
}
double calculate(int i1,int j1,int i2,int j2,int n)
{
if(d[i1][j1][i2][j2][n]>=0)
return d[i1][j1][i2][j2][n];
if(n==1)
return s[i1][j1][i2][j2];
double mini=99999999999;
double temp_min;
int i,j;
//下面这两个循环判断横切还是竖切
for(i=i1;i<i2;i++) //判断横切是取上半部分还是下半部分
{
temp_min=MY_min( calculate(i1,j1,i,j2,n-1)+sum_area(i+1,j1,i2,j2) , calculate(i+1,j1,i2,j2,n-1)+sum_area(i1,j1,i,j2) );
if(temp_min<mini)
mini=temp_min;
}
for(j=j1;j<j2;j++) //判断竖切是取左半部分还是右半部分
{
temp_min=MY_min( calculate(i1,j1,i2,j,n-1)+sum_area(i1,j+1,i2,j2) , calculate(i1,j+1,i2,j2,n-1)+sum_area(i1,j1,i2,j) );
if(temp_min<mini)
mini=temp_min;
}
d[i1][j1][i2][j2][n]=mini;//保存状态
return d[i1][j1][i2][j2][n];
}
int main()
{
int n;
int i,j;
double sum=0;
double mini;
scanf("%d",&n);
for(i=1;i<=8;i++)
{
for(j=1;j<=8;j++)
{
scanf("%lf",&map[i][j]);
sum+=map[i][j];
}
}
//两个数组都先初始化为-1,方便之后每一次判断避免重复运算
memset(d,-1,sizeof(d));
memset(s,-1,sizeof(s));
mini=calculate(1,1,8,8,n);
sum/=(n*1.0);
printf("%.3lf/n",sqrt(mini/(n*1.00)-(sum*sum)));
return 0;
}