BZOJ 1048: [HAOI2007]分割矩阵
1048: [HAOI2007]分割矩阵
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 1077 Solved: 776
[Submit][Status][Discuss]
Description
将一个a*b的数字矩阵进行如下分割:将原矩阵沿某一条直线分割成两个矩阵,再将生成的两个矩阵继续如此分割(当然也可以只分割其中的一个),这样分割了(n-1)次后,原矩阵被分割成了n个矩阵。(每次分割都只能沿着数字间的缝隙进行)原矩阵中每一位置上有一个分值,一个矩阵的总分为其所含各位置上分值之和。现在需要把矩阵按上述规则分割成n个矩阵,并使各矩阵总分的均方差最小。请编程对给出的矩阵及n,求出均方差的最小值。
Input
第一行为3个整数,表示a,b,n(1<a,b<=10,1<n<=10)的值。
第二行至第n+1行每行为b个小于100的非负整数,表示矩阵中相应位置上的分值。每行相邻两数之间用一个空
格分开。
Output
仅一个数,为均方差的最小值(四舍五入精确到小数点后2位)
Sample Input
5 4 4
2 3 4 6
5 7 5 1
10 4 0 5
2 0 2 3
4 1 1 1
Sample Output
0.50
题解
方差公式为sqrt(((x1-ave)^2+(x2-ave)^2+…+(xn-ave)^2)/n)
ave可以直接得到。
因为a,b,n<=10,那么可以直接记忆化搜索,设f[x1][y1][x2][y2][k]为以(x1,y1)为左上角,(x2,y2)为右下角的矩形分成k个矩形最小的(s-ave)^2的和(s为k个小矩形各自的分值)。
那么最终答案是sqrt(f[1][1][a][b][n]/n)。
代码
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> using namespace std; const int N=15,inf=0x3f3f3f3f; int a,b,n,sum; int map[N][N],s[N][N]; double ave; double f[N][N][N][N][N]; double sqr(double x){ return x*x; } double dfs(int x1,int y1,int x2,int y2,int n){ if(f[x1][y1][x2][y2][n]!=-1)return f[x1][y1][x2][y2][n]; f[x1][y1][x2][y2][n]=inf; if(n==1){ return f[x1][y1][x2][y2][1]=sqr((s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1])-ave); } for(int i=x1;i<x2;i++){ for(int j=1;j<n;j++){ f[x1][y1][x2][y2][n]=min(f[x1][y1][x2][y2][n],dfs(x1,y1,i,y2,j)+dfs(i+1,y1,x2,y2,n-j)); } } for(int i=y1;i<y2;i++){ for(int j=1;j<n;j++){ f[x1][y1][x2][y2][n]=min(f[x1][y1][x2][y2][n],dfs(x1,y1,x2,i,j)+dfs(x1,i+1,x2,y2,n-j)); } } return f[x1][y1][x2][y2][n]; } int main(){ scanf("%d%d%d",&a,&b,&n); for(int i=1;i<=a;i++){ for(int j=1;j<=b;j++){ scanf("%d",&map[i][j]); sum+=map[i][j]; s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+map[i][j]; } } ave=(double)sum/n; for(int i=1;i<=a;i++){ for(int j=1;j<=b;j++){ for(int k=i;k<=a;k++){ for(int l=j;l<=b;l++){ for(int m=1;m<=n;m++){ f[i][j][k][l][m]=-1; } } } } } printf("%.2lf",sqrt(dfs(1,1,a,b,n)/n)); return 0; }