均方差公式比较复杂,先将其变形
S^2 = 1/n∑(Xi - X)^2
= 1/n(n*X^2 + ∑Xi^2 - 2*X∑Xi)
= 1/n∑Xi^2 - X^2;
X表示均值
由于均值是一定的(它等于方格里的数和除以n),所以只需要让每个矩形的总分的平方和尽量小
对于左上角(x1,y1),右下角(x2,y2)的正方形,设它的总和为S[x1,y1,x2,y2]
切割K次后得到的K+1块矩形的总分平方和最小值为dp[k,x1,y1,x2,y2]
则它可以横着切,也可以竖着切,然后选一块切(这里用到了递归!)
dp[k,x1,y1,x2,y2] =min{
min{dp[k-1,x1,y1,a,y2]+S[a+1,y1,x2,y2] , dp[k-1,a+1,y1,x2,y2]+S[x1,y1,a,y2]}(x1<=a<x2),
min{dp[k-1,x1,y1,x2,b]+S[x1,b+1,x2,y2] , dp[k-1,x1,b+1,x2,y2]+S[x1,y1,x2,b]}(y1<=b<y2)
}
设m为棋盘边长,则状态数目为m^4*n,决策数目为O(m)
O(m^5*n))
一道比较高级的动态规划题目,真是有点困难,要注意,x,y,xx,yy所表达的意思,特别控制循环条件不能错(含右或含下线)
#include <iostream>
#include <string.h>
#include <vector>
#include <queue>
#include <stdio.h>
#include <cstdlib>
#include <math.h>
using namespace std;
int n;
int min(int a,int b)
{
return a<b?a:b;
}
int sum[9][9][9][9],dp[16][9][9][9][9],gp[9][9];
void init()
{
for(int x=0;x<8;x++)
for(int y=0;y<8;y++)
for(int xx=x;xx<8;xx++)
for(int yy=y;yy<8;yy++)
{
int ans=0;
for(int i=x;i<=xx;i++)
for(int j=y;j<=yy;j++)
{
ans+=gp[i][j];
}
sum[x][y][xx][yy]=ans*ans;
sum[xx][y][x][yy]=ans*ans;
sum[xx][yy][x][y]=ans*ans;
sum[x][yy][xx][y]=ans*ans;
}
}
int DP(int k,int x,int y,int xx,int yy)
{
if(k==n-1)
return sum[x][y][xx][yy];
if(dp[k][x][y][xx][yy]>=0)
return dp[k][x][y][xx][yy];
int ans=0;
dp[k][x][y][xx][yy]=1<<29;
for(int i=x;i<xx;i++)
{
ans=min(DP(k+1,x,y,i,yy)+sum[i+1][y][xx][yy],DP(k+1,i+1,y,xx,yy)+sum[x][y][i][yy]);
dp[k][x][y][xx][yy]=min(ans,dp[k][x][y][xx][yy]);
}
for(int i=y;i<yy;i++)
{
ans=min(DP(k+1,x,y,xx,i)+sum[x][i+1][xx][yy],DP(k+1,x,i+1,xx,yy)+sum[x][y][xx][i]);
dp[k][x][y][xx][yy]=min(ans,dp[k][x][y][xx][yy]);
}
return dp[k][x][y][xx][yy];
}
int main()
{
int cnt=0;
double ans;
cin>>n;
for(int i=0;i<8;i++)
{
for(int j=0;j<8;j++)
{
cin>>gp[i][j];
cnt+=gp[i][j];
}
}
init();
memset(dp,-1,sizeof(dp));
DP(0,0,0,7,7);
ans=sqrt(dp[0][0][0][7][7]*1.0/n-((cnt*1.0)/n)*((cnt*1.0)/n));
printf("%.3f\n",ans);
return 0;
}