BZOJ1048 [HAOI2007]分割矩阵
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
2 3 4 6
5 7 5 1
10 4 0 5
2 0 2 3
4 1 1 1
Sample Output
0.50
题解
明明是标准差为什么要写方差!!!
a,b,n都那么小,直接上$O(a^2b^2(a+b)n^2)$DP。。。
因为方差等于平方的期望减期望的平方(期望在这里就是平均值),而后者是固定的(总和除以n),只需要求平方和最小即可。
定义$f_{k,u,d,l,r}$为将上边界为u,下边界为d,左为l,右为r的矩阵切成k块后的平方和最小是多少,那么只需要枚举第一次从哪一行(或列)切和切成的两半各分多少份即可。
状态$O(a^2b^2n)$个,转移$O(n(a+b))$。
简单说,大概是$O(n^7)$的,如果a,b,n同阶的话。
附代码:
#include <algorithm> #include <cmath> #include <cstdio> using std::min; typedef double LL; const int N = 15; LL s[N][N], f[N][N][N][N][N]; inline LL sqr(LL x) { return x * x; } inline LL sum(int l, int r, int u, int d) { return s[d][r] - s[d][l - 1] - s[u - 1][r] + s[u - 1][l - 1]; } int main() { int a, b, n; scanf("%d%d%d", &a, &b, &n); for (int i = 1; i <= a; ++i) for (int j = 1; j <= b; ++j) { scanf("%lf", &s[i][j]); s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1]; } for (int u = 1; u <= a; ++u) for (int d = u; d <= a; ++d) for (int l = 1; l <= b; ++l) for (int r = l; r <= b; ++r) f[1][u][d][l][r] = sqr(sum(l, r, u, d)); for (int k = 2; k <= n; ++k) for (int hei = 1; hei <= a; ++hei) for (int u = 1; u + hei - 1 <= a; ++u) { int d = u + hei - 1; for (int wid = 1; wid <= b; ++wid) for (int l = 1; l + wid - 1 <= b; ++l) { int r = l + wid - 1; LL &ans = f[k][u][d][l][r]; ans = 1000000000000000000LL; for (int k1 = 1; k1 < k; ++k1) { int k2 = k - k1; for (int i = u + 1; i <= d; ++i) ans = min(ans, f[k1][u][i - 1][l][r] + f[k2][i][d][l][r]); for (int j = l + 1; j <= r; ++j) ans = min(ans, f[k1][u][d][l][j - 1] + f[k2][u][d][j][r]); } } } printf("%.2lf\n", sqrt(f[n][1][a][1][b] / n - sqr(s[a][b] / n))); return 0; }