Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 14504 | Accepted: 5191 |
Description

原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。
均方差
请编程对给出的棋盘及n,求出O'的最小值。
Input
第2行至第9行每行为8个小于100的非负整数,表示棋盘上相应格子的分值。每行相邻两数之间用一个空格分隔。
Output
Sample Input
3 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 0 3
Sample Output
1.633
今天来研究一下dp,实际上这个比较难想,感觉纯自己想想不出来,因为我想了一天也没什么进展。看到人家那直接上的5维........自己的思维总是在1维上,二维都不会转,还是要多刷题多总结这样的题型才行啊.
思路:先来看一下我们要求的方差这个公式吧。
这里x表示平均值
我们先把每一项展开(Xi-x)^2=Xi^2 - 2*Xi*x+x^2
然后n项的和就是(X1^2+X2^2.......+Xn^2)-(2X1x+2X2x........+2Xnx)+nx^2.
因为X1+X2+....+Xn为矩阵的总和,就等于x*n
所以(2X1x+2X2x........+2Xnx)=2*x*n*x
所以原式就可以变为:(X1^2+X2^2.......+Xn^2)-nx^2
得到σ^2=1/n∑xi^2 - x^2。
所以要使得方差最小只要使得∑xi^2最小就可以了。
dp[k][x1][y1][x2][y2] = min{
dp[0][x1][y1][t][y2]+dp[k-1][t+1][y1][x2][y2], (x1 <= t < x2)
dp[k-1][x1][y1][t][y2]+dp[0][t+1][y1][x2][y2], (x1 <= t < x2) //竖切
dp[0][x1][y1][x2][t]+dp[k-1][x1][t+1][x2][y2], (y1 <= t < y2)
dp[k-1][x1][y1][x2][t]+dp[0][x1][t+1][x2][y2] (y1 <= t < y2) //横切
}
dp[0][x1][y1][x2][y2]表示左上角坐标(x1,y1)到右下角坐标(x2,y2)区域的棋盘的分值和平方
注意:poj玄学问题,用g++交的时候输出double要用 f 而不是 lf。
实际上就是从没被切过的状态依次从下向上dp,先通过0次的制造出切过1次的,然后下次利用切过1,0次的制造切过2次的然后......
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int MAXN=10;
int n;
int num[MAXN][MAXN];
int sum_num[MAXN][MAXN];//(1,1)到(x,y)所组成的矩阵的和
double dp[15][MAXN][MAXN][MAXN][MAXN];
double get_sum(int x1,int y1,int x2,int y2)//得到左上角为(x1,y1)右下角为(x2,y2)的矩阵的和(不懂可以自己画图理解一下)
{
double ans=double(sum_num[x2][y2]-sum_num[x1-1][y2]-sum_num[x2][y1-1]+sum_num[x1-1][y1-1]);
return ans*ans;
}
void init()//一开始的所有矩阵都是被切了0的状态
{
for(int x1=1;x1<=8;++x1)
for(int y1=1;y1<=8;++y1)
for(int x2=x1;x2<=8;++x2)
for(int y2=y1;y2<=8;++y2)
{
dp[0][x1][y1][x2][y2]=get_sum(x1,y1,x2,y2);
}
}
void get_dp()//从下向上来达到下一级的状态
{
for(int k=1;k<n;++k)
for(int x1=1;x1<=8;++x1)
for(int y1=1;y1<=8;++y1)
for(int x2=x1;x2<=8;++x2)
for(int y2=y1;y2<=8;++y2)
{
int t;
dp[k][x1][y1][x2][y2]=1e9;
for(t=x1;t<x2;++t)
{
dp[k][x1][y1][x2][y2]=min(dp[k][x1][y1][x2][y2],dp[k-1][x1][y1][t][y2]+dp[0][t+1][y1][x2][y2]);
dp[k][x1][y1][x2][y2]=min(dp[k][x1][y1][x2][y2],dp[0][x1][y1][t][y2]+dp[k-1][t+1][y1][x2][y2]);
}
for(t=y1;t<y2;++t)
{
dp[k][x1][y1][x2][y2]=min(dp[k][x1][y1][x2][y2],dp[k-1][x1][y1][x2][t]+dp[0][x1][t+1][x2][y2]);
dp[k][x1][y1][x2][y2]=min(dp[k][x1][y1][x2][y2],dp[0][x1][y1][x2][t]+dp[k-1][x1][t+1][x2][y2]);
}
}
}
int main()
{
while(~scanf("%d",&n))
{
int i,j;
int sum=0;
for(i=1;i<=8;++i)
for(j=1;j<=8;++j)
{
scanf("%d",&num[i][j]);
sum_num[i][j]+=sum_num[i-1][j]+sum_num[i][j-1]-sum_num[i-1][j-1]+num[i][j];
sum+=num[i][j];
}
init();
get_dp();
double ans=dp[n-1][1][1][8][8]*1.0/n-(sum*1.0/n)*(sum*1.0/n);
ans=sqrt(ans);
printf("%.3f\n",ans);
}
return 0;
}