题意:中文题。给定一个8*8的64个格子,每个格子里有数字。要求将这个正方形切成n个长方形,使得各个长方形中数字之和的方差最小。
思路:dp,记忆化搜索。注意将方差公式写成每项平方的平均数减去平均数的平方的形式。后面一项是常数,所以只需要将前面一项最小化即可。dp(a,b,c,d,m)表示将左上角为(a,b),右下角为(c,d)组成的长方形分成m块的最优值。
此外还要能给定左上角点和右下角点快速求出矩形的数字之和,代码中s数组保存那个点到(1,1)的矩形的数字之和。
#include <cstdio> #include <algorithm> #include <cstring> #include <cmath> #include <cstdio> #define clr(s,t) memset(s,t,sizeof(s)) using namespace std; #define INF 0x3fffffff #define n 8 int m; int s[10][10],dp[n+2][n+2][n+2][n+2][18]; int sum2(int a,int b,int c,int d){ int res = s[c][d]-s[a-1][d]-s[c][b-1]+s[a-1][b-1]; return res*res; } int solve(int a,int b,int c,int d,int m){ int i,res = INF; if(dp[a][b][c][d][m]) return dp[a][b][c][d][m]; if(m==1) return dp[a][b][c][d][m] = sum2(a, b, c, d); if(a==c && (d-b)<m) //一个小剪枝 return dp[a][b][c][d][m] = INF; if(b==d && c-a<m) return dp[a][b][c][d][m] = INF; for(i = a;i<=c-1;i++){ //横切一刀 res = min(res,solve(a, b, i, d, m-1) + sum2(i+1,b,c,d)); res = min(res,sum2(a,b,i,d) + solve(i+1, b, c, d, m-1)); } for(i = b;i<=d-1;i++){ //竖切一刀 res = min(res,solve(a, b, c, i, m-1) + sum2(a,i+1,c,d)); res = min(res,sum2(a,b,c,i) + solve(a, i+1, c, d, m-1)); } return dp[a][b][c][d][m] = res; } int main(){ int i,j,sum = 0; double res = 0; clr(dp, 0); clr(s, 0); scanf("%d",&m); for(i = 1;i<=n;i++) for(j = 1;j<=n;j++){ scanf("%d",&s[i][j]); sum += s[i][j]; s[i][j] += s[i-1][j]+s[i][j-1]-s[i-1][j-1]; } res = ((double)sum/m); printf("%.3lf\n",sqrt((double)solve(1,1,n,n,m)/m-res*res)); return 0; }