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

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;
}

  

posted @ 2017-06-12 20:16  _rqy  阅读(528)  评论(0编辑  收藏  举报