题目连接:http://poj.org/problem?id=1191
题目大意:讲一个8*8的棋盘进行分割:将原棋盘割下一块矩形棋盘并保证剩下的棋盘也是矩形,再将剩下的部分继续如此分割,这样割了n-1次后,连通最后剩下的棋盘共有n块矩形棋盘。每次切割都沿着棋盘格子的边进行。棋盘上每一格子都有一个分值,一块矩形棋盘的总分为其所含各个格子分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。
给出棋盘及n,求最小的均方差。
题目分析:根据均方差的定义可以将其展开,从而得到均方差其实最终等于各块平方和-均值的平方然后再开平方。这个很容易推倒,由于数学公式不好打,所以具体过程就不写了,很容易推得。所以次问题就转化为求各块平方和的最小值,即找一种切割方式使得各块分值和的均方差最小即可。
说白了这个题就是枚举各种切割方法,然后累计切割次数即可。
代码如下:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> using namespace std; const double maxn=999999999; //说明下,本题中的矩形表示为用左上角的顶点左边和右下角的顶点坐标确定一个矩形 double sum[8][8][8][8];//dp[i][j][x][y]枚举记录每个矩形(i,j)(x,y)的和值 double dp[15][8][8][8][8];//dp[k][i][j][x][y]表示第k层矩形为(i,j)(x,y)的矩形和值的平方 int map[8][8]; double total; double average; double min(double a,double b){ return a<b?a:b; } int main() { int n; total=0; scanf("%d",&n); for (int i=0;i<8;++i) { for (int j=0;j<8;++j) { scanf("%d",&map[i][j]); total+=map[i][j]; } } average=total/(n*1.0); for (int i=0;i<8;++i) { for (int j=0;j<8;++j) { for (int x=i;x<8;++x) { total=0; for (int y=j;y<8;++y)//四重循环来求所有可能的矩形的块的分值 { total+=map[x][y]; if (x==i) { sum[i][j][x][y]=total; } else { sum[i][j][x][y]=total+sum[i][j][x-1][y]; } } } } } for (int i=0;i<8;++i) { for (int j=0;j<8;++j) { for (int x=i;x<8;++x) { for (int y=j;y<8;++y)//此处四重循环就是将前面的和值转化为平方然后赋值给DP数组 { sum[i][j][x][y]*=sum[i][j][x][y]; dp[0][i][j][x][y]=sum[i][j][x][y]; } } } } for (int k=1;k<n;++k) { for (int i=0;i<8;++i) { for (int j=0;j<8;++j) { for (int x=i;x<8;++x) { for (int y=j;y<8;++y) { dp[k][i][j][x][y]=maxn; double temp; for (int mid=i;mid<x;++mid)//竖着枚举分割线 { temp=min(dp[k-1][i][j][mid][y]+sum[mid+1][j][x][y],dp[k-1][mid+1][j][x][y]+sum[i][j][mid][y]); dp[k][i][j][x][y]=min(temp,dp[k][i][j][x][y]); } for(int mid=j;mid<y;++mid){//横着枚举分割线 temp=min(dp[k-1][i][j][x][mid]+sum[i][mid+1][x][y],dp[k-1][i][mid+1][x][y]+sum[i][j][x][mid]); dp[k][i][j][x][y]=min(temp,dp[k][i][j][x][y]); }//经过横着和竖着枚举分割线得出最优进行记录 } } } } } double ans=dp[n-1][0][0][7][7]/(n*1.0)-average*average; //cout<<ans<<endl; printf("%.3lf\n",sqrt(ans));//保留三位 return 0; }