棋盘分割可以说是DP问题的经典,在刘汝佳老师的《算法艺术与信息学竞赛》上给出的一道例题,分析公式,要使最后的西格玛最小只要使每个方格的分数的平方和最小就行了,因为平均值使一个定值;
思路:
1,先化简均方差公式,可以看出,只需要让每个分割后的矩形的总分的平方和尽量小,即可使均方差最小。
2,考虑左上角坐标为(x1,y1),右下角坐标为(x2,y2)的棋盘,设它的总和为value[x1,y1,x2,y2]切割k次以后得到k+1块矩形的总分平方和最小值为store[k,x1,y1,x2,y2],则它可以沿着横线切,也可以沿着竖线切,然后选一块继续切(递归)。。
3,由1,2部可以得到状态转移方程:
store[k,x1,y1,x2,y2]=min{
min{store[k-1,x1,y1,row,y2]+value[row+1,y1,x2,y2]^2,store[k-1,row+1,y1,x2,y2]+value[x1,y1,row,y2]^2},
min{store[k-1,x1,y1,x2,column]+value[x1,column+1,x2,y2]^2,store[k-1,x1,column+1,x2,y2]+value[x1,y1,x2,column]^2}}
其中:(x1<=row<x2),(y1<=column<y2);
初始值,对于k==0,store[k,x1,y1,x2,y2]=value[x1,y1,x2,y2]^2;
#include <iostream>
#include<iomanip>
#include<cmath>
using namespace std;
int n;
int sum[9][9];
const int Max = 9;
int store[15][9][9][9][9];
int min(int a,int b)
{
return a > b ? b:a;
}
int value(int x1,int y1,int x2,int y2)
{
return sum[x2][y2] - sum[x2][y1-1] - sum[x1-1][y2] + sum[x1-1][y1-1];
}
void input()
{
int i, j, part,wt;
cin >> n;
for (i = 0;i < Max; i++)
{
sum[0][i] = 0;
sum[i][0] = 0;
}
for (i = 1;i < Max; i++)
for (j = 1, part = 0;j < Max; j++)
{
cin >> wt;
part += wt;
sum[i][j] = sum[i-1][j] + part;
}
}
int solve(int k,int x1,int y1,int x2,int y2)
{
int s0 ,s1, s ,row, column, mul;
int Min = 99999999;
if (k == 0 )
{
s = value(x1,y1,x2,y2);
store[k][x1][y1][x2][y2] = s*s;
return s*s;
}
if (store[k][x1][y1][x2][y2] != -1)
return store[k][x1][y1][x2][y2];
for (row = x1; row < x2; row++)
{
s0 = value(x1, y1, row, y2);
s1 = value(row+1, y1, x2, y2);
mul = min(solve(k-1,x1,y1,row,y2)+ s1*s1 ,solve(k-1,row+1,y1,x2,y2) + s0*s0);
if (mul < Min)
Min = mul;
}
for (column= y1;column < y2; column++)
{
s0 = value(x1, y1, x2, column);
s1 = value(x1, column+1, x2, y2);
mul = min((solve(k-1,x1, y1, x2, column) + s1*s1),(solve(k-1,x1,column+1, x2, y2) + s0*s0));
if (mul < Min)
Min = mul;
}
store[k][x1][y1][x2][y2] = Min;
return Min;
}
int main ()
{
input();
memset(store,-1,sizeof(store));
int temp = solve(n-1,1,1,8,8);
int total = sum[8][8] * sum[8][8];
temp =temp*n - total;
double p = sqrt((temp*1.0)/(n*n*1.0));
cout<<setiosflags(ios::fixed)<<setprecision(3)<<p<<endl;
return 0;
}