ACM PKU 1191 棋盘分割 http://acm.pku.edu.cn/JudgeOnline/problem?id=1191
棋盘分割可以说是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; }