题意:
- 一个矩阵可以横切,或者侧切,如图所示
- 对于被切开成两个部分的棋盘,可以且只能选择一个继续向下切
求方差最小,由于均方差X固定,n固定,要计算的是被分成的子矩形的面积(xi-X)*(xi-X)。
所以,可以采用搜索的方法,不断地枚举出最小值
分析:二维区间dp,其实类似于数字三角形模型,由于分割可能不具有线性递推的关系,所以采用记忆化搜索的方法。
- 横切i,分为两部分[(x1,y1)~(i,y2)],[(i+1,y2)~(x2,y2)]
- 竖切i,分为两部分[(x1,y1)~(x2,i)],[(x2,i+1)~(x2,y2)]
初始化:全部初始化为无穷大,由于计划化搜索特殊性,所以先初始化为负数,进入是再初始化
状态转移方程/状态计算:
根据横切竖切的位置i不同,和划分后继续划分哪个子集,可以划分为多个集合
横切,划分了两个子集,两个子集都可以继续划分,另一个直接计算
- dp[x1][y1][x2][y2][k]=max(dp[x1][i][x2][y2][k]+sum[x1~i][y1~y2]);
- dp[x1][y1][x2][y2][k]=max(dp[x1][y1][i+1][y2][k]+sum[i+1~]
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int N=16;
const int inf=1e9;
int s[N][N];
double dp[N][N][N][N][N];
double X;
double get(int x1,int y1,int x2,int y2)
{
int a=s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1];
return (a-X)*(a-X);
}
double dfs(int x1,int y1,int x2,int y2,int k)
{
double &v=dp[x1][y1][x2][y2][k];//为了简便,加上v
if(v>=0) return v;
if(k==1) return get(x1,y1,x2,y2);
v=1e9; //求最小值,初始化为无穷
for(int i=x1;i<x2;i++)
{//横切的子集
v=min(v,dfs(i+1,y1,x2,y2,k-1)+get(x1,y1,i,y2));
v=min(v,dfs(x1,y1,i,y2,k-1)+get(i+1,y1,x2,y2));
}
for(int i=y1;i<y2;i++)
{//竖切的子集
v=min(v,dfs(x1,i+1,x2,y2,k-1)+get(x1,y1,x2,i));
v=min(v,dfs(x1,y1,x2,i,k-1)+get(x1,i+1,x2,y2));
}
return v;
}
int main()
{
int n;
cin>>n;
for(int i=1;i<=8;i++)
for(int j=1;j<=8;j++)
cin>>s[i][j];
for(int i=1;i<=8;i++)
for(int j=1;j<=8;j++)
s[i][j]+=s[i][j-1]+s[i-1][j]-s[i-1][j-1];
X=(double)s[8][8]/n;
memset(dp,-1,sizeof dp); //由于积分可能是0,所以取-1
double a=dfs(1,1,8,8,n)/n;
printf("%.3lf",sqrt(a));
return 0;
}