P5760

容易想到设 $f_{i, j}$ 表示前 $i$ 块积木,把它们分成 $j$ 堆柱子后高度之和的最大值 (第 $i$ 块积木在第 $j$ 堆柱子),

然后考虑第 $i$ 块三种情况:

  1. 不放,什么都不用做

  2. 另起一个柱子,枚举第 $j - 1$ 堆柱子的顶端 $k$,则

    $$f_{i, j} = \max(f_{i, j}, f_{k, j - 1} + high_i)$$

  3. 加在前一个柱子之前,枚举第 $i$ 块积木在第 $k$ 块积木上,则

    $$f_{i, j} = \max(f_{i, j}, f_{k, j} + high_i)$$

其中,$high_i$ 为第 $i$ 块积木的高度,

高高兴兴地写完,交!听取 WA 声一片啊!

仔细想想:长方体可以通过旋转得到另一个长方体!

一个长方体可以最多可以 $3$ 个不同的长方体,

令类型 $0$ 为以 $a, b$ 为底的长方体,

类型 $1$ 为以为底的 $b, c$ 长方体,

类型 $2$ 为以 $c, a$ 为底的长方体

其中,$a, b, c$ 为题目中的意思,

设 $f_{i, j, d}$ 表示前 $i$ 块积木,把它们分成 $j$ 堆柱子,第 $i$ 块积木类型为 $d$,的高度之和的最大值 (第 $i$ 块积木在第 $j$ 堆柱子)

这里 $0 \leq d \leq 2$,

同上,分三种情况:

  1. 不放,什么都不用做

  2. 另起一个柱子,枚举第 $j - 1$ 堆柱子的顶端 $k$,则

    $$f_{i, j, d1} = \max(f_{i, j, d1}, f_{k, j - 1, d2} + high_{i, d1})$$

  3. 加在前一个柱子之前,枚举第 $i$ 块积木在第 $k$ 块积木上,则

    $$f_{i, j, d1} = \max(f_{i, j, d1}, f_{k, j, d2} + high_{i,d1})$$

其中,$d1, d2$ 分别为 $i$ 的类型和 $k$ 的类型,$high_{i, d1}$ 为 $i$ 为 $d1$ 类型时的高度。

以下优化为 @asdfo123 大佬的方法:

为了方便,令 $l_{i,0},l_{i,1},l_{i,2}$ 分别为题目所说的 $a, b, c$,

在令$$l_{i, 3} = l_{i, 0}$$

$$l_{i, 4} = l_{i, 1}$$

则类型为 $d$ 的第 $i$ 个长方体的高为 $l_{i, d +2}$,长宽为 $l_{i, d}$ 以及 $l_{i, d + 1}$,

这样在 $high_{i, d}$ 时和处理两个长方体堆在一起时是否合法就方便多了。

#include <cstdio>
#include <iostream>
using namespace std;

const int N = 110;

int l[N][5], f[N][N][5];

int length (int x, int d) { // 计算第 x 个长方体 d 类型的长方体的长
    return max (l[x][d], l[x][d + 1]);
}

int width (int x, int d) { // 计算第 x 个长方体 d 类型的长方体的宽
    return min (l[x][d], l[x][d + 1]);
}

bool check (int x, int d1, int y, int d2) { // 判断上下放置是否合法
    return (length (x, d1) <= length (y, d2)) && (width (x, d1) <= width (y, d2));
}

int main() {
    int n, m; scanf ("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i) {
        scanf ("%d%d%d", &l[i][0], &l[i][1], &l[i][2]);
        l[i][3] = l[i][0]; l[i][4] = l[i][1];
    }
    int ans = 0;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            for (int k = 0; k < i; ++k) {
                for (int d1 = 0; d1 <= 2; ++d1) {
                    for (int d2 = 0; d2 <= 2; ++d2) {
                        f[i][j][d1] = max (f[i][j][d1], f[k][j - 1][d2] + l[i][d1 + 2]); // 另起一堆柱子
                        if (check (i, d1, k, d2)) {
                            f[i][j][d1] = max (f[i][j][d1], f[k][j][d2] + l[i][d1 + 2]); // 放在在以前的柱子上面
                        }
                        ans = max (ans, f[i][j][d1]);
                    }
                }
            }
        }
    }
    printf ("%d", ans);
    return 0;
}
posted @ 2022-04-13 08:58  wangzhongyuan  阅读(17)  评论(0)    收藏  举报  来源