http://poj.org/problem?id=1191
(1)棋盘任意分割不是这道题所要讨论的,注意到题目中提到的“不允许的分割”,故会出现
s=min(s, d(n-1, x1, y1, i, y2)+sum(i+1, y1, x2, y2));
s=min(s, sum(x1, y1, i, y2)+d(n-1, i+1, y1, x2, y2));
这样的规划关系(横向分割时)。
(2)比较两种写法:
1) float ans=d(k, 1, 1, 8, 8);
ans=sqrt((float)ans/k-pow(sqrt(sum(1,1,8,8))/k, 2.0));
printf("%.3f\n", ans);
2) float a, b,s;
a=d(k,1,1,8,8), b=sqrt(sum(1,1,8,8))/k;
s=sqrt(a/k-b*b);
printf("%.3f\n", s);
后者更优(0ms, 16ms), 且更不易出错。
(3)以(x1, y1, x2, y2)的形式描述矩形;
(4)将 data[i][j] 预处理成从左上角起的矩阵数字和,具体实现:
for(i=1;i<=8;i++) for(j=1;j<=8;j++) { scanf("%d", &data[i][j]); data[i][j]+=data[i-1][j]+data[i][j-1]-data[i-1][j-1]; }
调用所需矩阵的和值:
s=data[x2][y2]-data[x1-1][y2]-data[x2][y1-1]+data[x1-1][y1-1];
(5)用到概率论的知识,只需使得最终各个小盘里数字和的平方和最小。
具体代码:

#include<stdio.h> #include<math.h> #include<string.h> #include<algorithm> using namespace std; int k, data[10][10]; int dp[16][10][10][10][10]; int sum(int x1, int y1, int x2, int y2) { int s=data[x2][y2]-data[x1-1][y2]-data[x2][y1-1]+data[x1-1][y1-1]; //数学知识 return s*s; } int d(int n, int x1, int y1, int x2, int y2) { int i, j; if(dp[n][x1][y1][x2][y2]!=-1) return dp[n][x1][y1][x2][y2]; if(n==1||x1==x2||y1==y2) //不能再分割 { dp[n][x1][y1][x2][y2]=sum(x1, y1, x2, y2); return dp[n][x1][y1][x2][y2]; } int s=(1<<29); for(i=x1;i<x2;i++) //横切 { s=min(s, d(n-1, x1, y1, i, y2)+sum(i+1, y1, x2, y2)); s=min(s, sum(x1, y1, i, y2)+d(n-1, i+1, y1, x2, y2)); } for(j=y1;j<y2;j++) //纵切 { s=min(s, d(n-1, x1, y1, x2, j)+sum(x1, j+1, x2, y2)); s=min(s, sum(x1, y1, x2, j)+d(n-1, x1, j+1, x2, y2)); } dp[n][x1][y1][x2][y2]=s; return s; } int main() { int i, j; while(scanf("%d", &k)!=EOF) { memset(data, 0, sizeof(data)); for(i=1;i<=8;i++) for(j=1;j<=8;j++) { scanf("%d", &data[i][j]); data[i][j]+=data[i-1][j]+data[i][j-1]-data[i-1][j-1]; //预处理,注意data[i][j]含义 } memset(dp, -1, sizeof(dp)); float a, b,s; a=d(k,1,1,8,8), b=sqrt(sum(1,1,8,8))/k; s=sqrt(a/k-b*b); printf("%.3f\n", s); } return 0; }