题意:中文题。给定一个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;
}
本文介绍了一种通过记忆化搜索实现的动态规划算法,用于解决如何将一个包含数字的8*8矩阵切割成多个长方形区域的问题,目标是最小化各区域数字总和的方差。文中详细解释了算法原理及其实现细节。
2251

被折叠的 条评论
为什么被折叠?



