http://poj.org/problem?id=1191
明白几点:
1)最终的均方差可以转变成:sqrt( sigma[ i:1-m ]( xi*xi ) / n - ( sum(all) / n )*( sum(all) / n ) ),不懂这个转换的需要补一下数学基础;所以最终决定最小值的是sigma[ i:1-m ]( xi*xi )。
2)动归含义:
dp[n][x1][x2][y1][y2]
表示由x1,x2,y1,y2组成的矩形要生成n块小矩阵,均方差的最小值(sigma[ i:1-m ]( xi*xi ));它的值需要遍历所有横向切、纵向切的可能,然后取最小值。最终结果是dp[n][1][8][1][8],不多说。
# include<iostream>
# include<string.h>
# include<cmath>
using namespace std;
# define N 16
# define INF 1000000000
int data[9][9];
int dp[N][9][9][9][9];
int onePart[9][9][9][9];
int OnePart(int x1, int x2, int y1, int y2)
{
int sum=0;
for(int x=x1;x<=x2;x++)
{
for(int y=y1;y<=y2;y++)
{
sum+=data[x][y];
}
}
return sum;
}
int Partition(int n, int x1, int x2, int y1, int y2)
{
if(dp[n][x1][x2][y1][y2]!=-1)
{
return dp[n][x1][x2][y1][y2];
}
else
{
int x,y,t,t1,t2;
dp[n][x1][x2][y1][y2]=INF;
//up-down
for(x=x1;x<x2;x++) //x<x2, not x<=x2
{
//continue partition up
t1=onePart[x+1][x2][y1][y2]+Partition(n-1,x1,x,y1,y2); //onePart[x+1][x2][y1][y2], not onePart[x][x2][y1][y2]
//continue partition down
t2=onePart[x1][x][y1][y2]+Partition(n-1,x+1,x2,y1,y2);
t = t1<t2? t1:t2 ;
dp[n][x1][x2][y1][y2] = dp[n][x1][x2][y1][y2]<t? dp[n][x1][x2][y1][y2]:t ;
}
//left-right
for(y=y1;y<y2;y++)
{
//continue partition left
t1=onePart[x1][x2][y+1][y2]+Partition(n-1,x1,x2,y1,y);
//continue partition right
t2=onePart[x1][x2][y1][y]+Partition(n-1,x1,x2,y+1,y2);
t = t1<t2? t1:t2 ;
dp[n][x1][x2][y1][y2] = dp[n][x1][x2][y1][y2]<t? dp[n][x1][x2][y1][y2]:t ;
}
return dp[n][x1][x2][y1][y2];
}
}
int main()
{
int x1,x2,y1,y2,x,y,n,t;
int sum=0;
cin>>n;
for(x=1;x<=8;x++)
{
for(y=1;y<=8;y++)
{
cin>>data[x][y];
sum+=data[x][y];
}
}
memset(dp,-1,sizeof(dp));
for(x1=1;x1<=8;x1++)
{
for(x2=x1;x2<=8;x2++)
{
for(y1=1;y1<=8;y1++)
{
for(y2=y1;y2<=8;y2++)
{
t=OnePart(x1,x2,y1,y2);
onePart[x1][x2][y1][y2]=t*t;
dp[1][x1][x2][y1][y2]=t*t;
}
}
}
}
Partition(n,1,8,1,8);
printf("%.3f\n", sqrt( dp[n][1][8][1][8]*1.0/n - (sum*1.0/n)*(sum*1.0/n) ) );
return 0;
}