最优连通子集,对于所要求的方差,展开,化简变成求大矩形分成n块,各块之和的平方的和的最小值,用记忆化搜索模拟每次切割的选择。
#include <iostream>
#include <fstream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
#define M 8
#define N 15
#define inf 10000000
int dp[M*M][M*M][N+5];
int sum[M*M][M*M];
int n;
int js(int x1,int y1,int x2,int y2)
{
int a,b,c,d;
b=c=d=0;
if (y1-1>=0) b=sum[x2][y1-1];
if (x1-1>=0) c=sum[x1-1][y2];
if (y1-1>=0 && x1-1>=0) d=sum[x1-1][y1-1];
a=sum[x2][y2]-b-c+d;
return a*a;
}
int dfs(int x1,int y1,int x2,int y2,int k)
{
//printf("dp[%d,%d,%d,%d]=%d\n",dp[x1*8+y1][x2*8+y2][k]);
if (k==0) {
dp[x1*8+y1][x2*8+y2][0]=js(x1,y1,x2,y2);
// printf("dp[%d,%d][%d,%d][%d]=%d\n",x1,y1,x2,y2,k,dp[x1*8+y1][x2*8+y2][k]);
return dp[x1*8+y1][x2*8+y2][0];
}
if (x2+y2-x1-y1<k) return inf;
if (dp[x1*8+y1][x2*8+y2][k]<inf) return dp[x1*8+y1][x2*8+y2][k];
//
for (int i=x1;i<x2;i++){
dp[x1*8+y1][x2*8+y2][k]=min(dp[x1*8+y1][x2*8+y2][k],
dfs(i+1,y1,x2,y2,k-1)+
js(x1,y1,i,y2));
dp[x1*8+y1][x2*8+y2][k]=min(dp[x1*8+y1][x2*8+y2][k],
dfs(x1,y1,i,y2,k-1)+
js(i+1,y1,x2,y2));
}
//
for (int j=y1;j<y2;j++){
dp[x1*8+y1][x2*8+y2][k]=min(dp[x1*8+y1][x2*8+y2][k],
dfs(x1,j+1,x2,y2,k-1)+
js(x1,y1,x2,j));
dp[x1*8+y1][x2*8+y2][k]=min(dp[x1*8+y1][x2*8+y2][k],
dfs(x1,y1,x2,j,k-1)+
js(x1,j+1,x2,y2));
}
//printf("dp[%d,%d][%d,%d][%d]=%d\n",x1,y1,x2,y2,k,dp[x1*8+y1][x2*8+y2][k]);
return dp[x1*8+y1][x2*8+y2][k];
}
int main()
{
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
cin>>n;
for (int i=0;i<M;i++)
for (int j=0;j<M;j++){
cin>>sum[i][j];
if (i==0) {
if (j) sum[0][j]+=sum[0][j-1];
}
else if (j==0) sum[i][0]+=sum[i-1][0];
else sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
//cout<<i<<','<<j<<' '<<sum[i][j]<<endl;
}
for (int i=0;i<M*M;i++)
for (int j=0;j<M*M;j++)
for (int k=0;k<=N;k++)
dp[i][j][k]=inf;
//cout<<dp[0][0][0]<<endl;
dfs(0,0,7,7,n-1);
printf("%.3f\n",sqrt(dp[0][63][n-1]*1.0/n-(sum[7][7]*1.0/n)*(sum[7][7]*1.0/n)) );
return 0;
}