题目链接:http://poj.org/problem?id=1191
//题目意思:在一个8*8的棋盘中要划分成n块,并且方差最小
//解题思路:用一个5维的数组来记录状态st[k][x1][y1][x2][y2],表示在第k次时从(x1,y1)到(x2,y2)的矩形切出的和的平方的和(就是已经分两块了,只是这个数组没表示从哪里切)。然后递归到前面一次,选择两个中的一块继续递归。
//0ms AC,代码如下:
#include<iostream>
#include<cstdio>
#include<math.h>
using namespace std;
#define min(a,b) ((a)<(b)) ? (a) : (b)
const int M =8;
int n;
int num[M+1][M+1]={0};//记录各个格子的值
int sum[M+1][M+1]={0};//记录(0,0)到(x,y)矩形的和
int st[15][M+1][M+1][M+1][M+1];
int sumFun(int x1,int y1,int x2,int y2)//返回矩形和的平方
{
int temp;
temp=sum[x2][y2]+sum[x1-1][y1-1]-sum[x1-1][y2]-sum[x2][y1-1];
return temp*temp;
}
int dp(int k,int x1,int y1,int x2,int y2)
{
int i,j,temp=1000000000;
if(st[k][x1][y1][x2][y2]!=-1)//已计算过了不用再算
return st[k][x1][y1][x2][y2];
else if(k==1||x1==x2||y1==y2)
{
return st[k][x1][y1][x2][y2]=sumFun(x1,y1,x2,y2);
}
else
{
for(i=x1;i<x2;i++)
{
temp=min(temp,min(sumFun(i+1, y1, x2, y2)+dp(k-1, x1, y1, i, y2),
sumFun(x1, y1, i, y2)+dp(k-1, i+1, y1, x2, y2)));
}
for(j=y1;j<y2;j++)
{
temp=min(temp, min(dp(k-1, x1, y1, x2, j)+sumFun(x1, j+1, x2, y2),
dp(k-1, x1, j+1, x2, y2)+sumFun(x1, y1, x2, j)));
}
st[k][x1][y1][x2][y2]=temp;
}
return temp;
}
void init()
{
scanf("%d",&n);
int i,j;
for(i=0;i<M+1;i++)
for(j=0;j<M+1;j++)
{
if(i==0||j==0)
{
num[i][j]=0;
sum[i][j]=0;
}
else
{
scanf("%d",&num[i][j]);
sum[i][j]=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]+num[i][j];
}
}
}
void makeSt()//st的初始化
{
int i,j,x,y,k;
for(k=0;k<15;k++)
for(i=0;i<=M;i++)
for(j=0;j<=M;j++)
for(x=0;x<=M;x++)
for(y=0;y<=M;y++)
st[k][i][j][x][y]=-1;
}
int main()
{
init();
makeSt();
double res=(double)sqrt((double)dp(n,1,1,M,M)/(double)n
-(double)pow((double)sqrt((double)sumFun(1,1,M,M))/(double)n,2.0));//之前因为没有强制转换为double,wa了好多次==!
printf("%.3lf\n",res);
return 0;
}