题意

给定一个 的矩阵,。求从矩阵中选出 个不相交小矩形,使得它们的权值和最大。

题解

先研究 的情况。显然这等价于经典的“最大 子段和”问题。可以在线性时间 内解决。

再研究 的情况。设 为前 行中选出了 个矩形可以得到的最大权值和。那么转移方程为
三个选项中取最大值。其中 表示第 列中,第 行到第 行之间的一段数的最大 子段和的值。这个可以在 时间内预处理得到。

初始条件为 。答案为

总的时间复杂度为 。应当可以优化到更低。

#include <bits/stdc++.h>
using namespace std;
int sumg[105] = {0}, sumh[105] = {0};
int n, m, k, a[105][2];
int g[105][105][12], h[105][105][12];
int f[105][12];
void solve1(){
    cout << g[1][n][k] << endl;
}
void solve2(){
    for (int i = 1; i <= n; ++i)
        sumh[i] = sumh[i - 1] + a[i][1];
    for (int i = 1; i <= n; ++i){
        for (int j = i - 1; j <= n; ++j)
            h[i][j][0] = 0;
        for (int t = 1; t <= k; ++t){
            int maxi = h[i][i - 1][t - 1] - sumh[i - 1];
            for (int j = i; j <= n; ++j){
                h[i][j][t] = max(h[i][j][t], sumh[j] + maxi);
                maxi = max(maxi, h[i][j][t - 1] - sumh[j]);
                h[i][j][t] = max(h[i][j][t], h[i][j - 1][t]);
            }
        }
    }
    for (int t = 1; t <= k; ++t){
        int maxi = f[0][t - 1];
        for (int i = 1; i <= n; ++i){
            f[i][t] = max(f[i][t], sumg[i] + sumh[i] + maxi);
            maxi = max(maxi, f[i][t - 1] - sumg[i] - sumh[i]);
            for (int j = i - 1; j >= 0; --j){
                for (int p = 1; p <= t; ++p)
                    for (int u = 0; u <= p; ++u)
                        f[i][t] = max(f[i][t], f[j][t - p] + g[j + 1][i][u] + h[j + 1][i][p - u]);       
            }
            f[i][t] = max(f[i][t], f[i - 1][t]);
        }
    }
    cout << f[n][k] << endl;
}
int main(){
    cin >> n >> m >> k;
    for (int i = 1; i <= n; ++i){
        for (int j = 0; j < m; ++j)
            cin >> a[i][j];
    }
    memset(f, 0xDF, sizeof(f));
    memset(g, 0xDF, sizeof(g));
    memset(h, 0xDF, sizeof(h));
    for (int i = 0; i <= n; ++i)
        f[i][0] = 0;
    for (int i = 1; i <= n; ++i)
        sumg[i] = sumg[i - 1] + a[i][0];
    for (int i = 1; i <= n; ++i){
        for (int j = i - 1; j <= n; ++j)
            g[i][j][0] = 0;
        for (int t = 1; t <= k; ++t){
            int maxi = g[i][i - 1][t - 1] - sumg[i - 1];
            for (int j = i; j <= n; ++j){
                g[i][j][t] = max(g[i][j][t], sumg[j] + maxi);
                maxi = max(maxi, g[i][j][t - 1] - sumg[j]);
                g[i][j][t] = max(g[i][j][t], g[i][j - 1][t]);
            }
        }
    }
    if (m == 1) solve1();
    else solve2();
    return 0;
}