首页 > 代码库 > BZOJ1084 [SCOI2005]最大子矩阵

BZOJ1084 [SCOI2005]最大子矩阵

Description

  这里有一个n*m的矩阵,请你选出其中k个子矩阵,使得这个k个子矩阵分值之和最大。注意:选出的k个子矩阵
不能相互重叠。

Input

  第一行为n,m,k(1≤n≤100,1≤m≤2,1≤k≤10),接下来n行描述矩阵每行中的每个元素的分值(每个元素的
分值的绝对值不超过32767)。

Output

  只有一行为k个子矩阵分值之和最大为多少。

Sample Input

3 2 2
1 -3
2 3
-2 3

Sample Output

9

题解

由于$1 \leq m \leq 2$,我们把m = 1和m = 2分开讨论:

若m = 1,经典$O(nk)$dp。

若m = 2,令$f_{i, j, k}(k \in \{0,1,2,3,4\})$表示前$i$行选$j$个不相交的子矩阵,且第$i$行放置状态为$k$的最大值,其中状态$0,1,2,3,4$分别代表:

0,第$i$行都没有选;1,第$i$行选了左边那个;2,第$i$行选了右边那个;3,第$i$行两个数在同一个子矩阵里;4,第$i$行两个数在两个不同的子矩阵里。

则我们有

$$\begin{aligned}
f_{i, j, 0} &= max(f_{i - 1, j, 0}, f_{i - 1, j, 1}, f_{i - 1, j, 2}, f_{i - 1, j, 3}, f_{i - 1, j, 4})\\
f_{i, j, 1} &= max(f_{i - 1, j - 1, 0}, f_{i - 1, j, 1}, f_{i - 1, j - 1, 2}, f_{i - 1, j - 1, 3}, f_{i - 1, j, 4})+x\\
f_{i, j, 2} &= max(f_{i - 1, j - 1, 0}, f_{i - 1, j - 1, 1}, f_{i - 1, j, 2}, f_{i - 1, j - 1, 3}, f_{i - 1, j, 4})+y\\
f_{i, j, 3} &= max(f_{i - 1, j - 1, 0}, f_{i - 1, j - 1, 1}, f_{i - 1, j - 1, 2}, f_{i - 1, j, 3}, f_{i - 1, j - 1, 4})+x+y\\
f_{i, j, 4} &= max(f_{i - 1, j - 1, 0}, f_{i - 1, j - 1, 1}, f_{i - 1, j - 1, 2}, f_{i - 1, j - 2, 3}, f_{i - 1, j, 4})+x+y
\end{aligned}$$

其中x和y分别是第$i$行的左右两个数。

代码:

#include <algorithm>
#include <cstdio>
using std::max;
int f[105][15][5];
inline int max(int a, int b, int c) {
  return max(max(a, b), c);
}
inline int max(int a, int b, int c, int d) {
  return max(max(a, b), max(c, d));
}
inline int max(int a, int b, int c, int d, int e) {
  return max(e, max(max(a, b), max(c, d)));
}
int main() {
  int n, m, k;
  scanf("%d%d%d", &n, &m, &k);
  if (m == 1) {
    int x;
    for (int i = 1; i <= n; ++i) {
      scanf("%d", &x);
      for (int j = 0; j <= k; ++j) {
        f[i][j][0] = max(f[i - 1][j][0], f[i - 1][j][1]);
        if (j) f[i][j][1] = max(f[i - 1][j - 1][0], f[i - 1][j][1]) + x;
      }
    }
    printf("%d\n", max(f[n][k][0], f[n][k][1]));
  } else {
    int x, y;
    for (int i = 1; i <= n; ++i) {
      scanf("%d%d", &x, &y);
      for (int j = 0; j <= k; ++j) {
        int *now = f[i][j], *las = f[i - 1][j], *las1 = f[i - 1][j - 1], *las2 = f[i - 1][j - 2];
        now[0] = max(las[0], las[1], las[2], las[3], las[4]);
        if (j) {
          now[1] = max(las1[0], las[1], las1[2], las1[3], las[4]) + x;
          now[2] = max(las1[0], las1[1], las[2], las1[3], las[4]) + y;
          now[3] = max(las1[0], las1[1], las1[2], las[3], las1[4]) + x + y;
          if (j > 1)
            now[4] = max(las2[0], las1[1], las1[2], las2[3], las[4]) + x + y;
        }
      }
    }
    int *ans = f[n][k];
    printf("%d\n", max(ans[0], ans[1], ans[2], ans[3], ans[4]));
  }
  return 0;
}

  

BZOJ1084 [SCOI2005]最大子矩阵