Loading

【题解】Luogu[P3360] 偷天换日

Link

solution

开题显然是个树形 dp,只不过在树形 dp 上又增加了背包问题。

我们不妨将每个走廊看成一个点,把交叉口看成边(当然也可以把交叉口看成点,不过写起来麻烦一些),于是就转化为了一棵二叉树。

我们设 \(f_{i,j}\) 表示以 \(i\) 为根的子树内,花费了不超过 \(j\) 时间,能拿到的最大价值。

考虑转移,发现输入是按深度给的,我们不妨一边 dfs 一边输入一边做转移。

对于当前节点(走廊) \(x\),我们两种情况分别讨论:

  1. 走廊的尽头是展览厅。
  2. 走廊的尽头是分叉口。

对于第一种,这里是一个显然的 01 背包问题,每幅画可以选择拿或不拿,代价为偷画的时间 \(c\),价值为画的价值 \(w\),即 \(f_{x,j}=\max(f_{x,j}, f_{x,j-c} + w)\),但是注意我们还需要付出走这条走廊的时间 \(t\),于是枚举 \(j\) 的时候注意要大于等于 \(t+c\)

对于第二种,我们不妨借鉴一下线段树的建树思想,令节点 \(i\) 的左儿子为 \(i\times 2\) 右儿子为 \(i\times 2 + 1\),我们枚举以 \(x\) 为根的子树内所花费的时间 \(i\),再枚举走左儿子最大花费的时间 \(j\),那么显然走右儿子最大花费时间能轻易算出来,为 \(i-j\) 吗?错,注意还需要花费时间走 \(x\) 这条走廊,故为 \(i-t-j\)。那么显然 \(f_{x,i} = \max(f_{x,i}, f_{x\times 2,j}+f_{x\times 2 + 1, i - t - j})\)

最后答案即为 \(f_{1,n}\)

坑点

  1. 注意每一条走廊我们不光需要走进去,还需要走出来回到出口,于是实际上每一条走廊需要走两遍,我们在输入的时候将该走廊需要花费的时间 \(\times 2\) 即可。
  2. 注意题目说警察会在 \(n\) 秒后到达出口,如果我们也在 \(n\) 秒后到达出口,显然会被警察抓住,于是我们需要在 \(n - 1\) 秒后到达出口,我们一开始将 \(n\) 减去 \(1\) 即可。

code

#include <bits/stdc++.h>
using namespace std;
int n, f[305][605], w[605], c[605];
void dfs(int x) {
    int t, m;
    cin >> t >> m, t *= 2;
    if(m) {
        for(int i = 1; i <= m; ++i)
            cin >> w[i] >> c[i];
        for(int i = 1; i <= m; ++i)
            for(int j = n; j >= t + c[i]; --j)
                f[x][j] = max(f[x][j - c[i]] + w[i], f[x][j]);
    } else {
        dfs(x * 2), dfs(x * 2 + 1);
        for(int i = t; i <= n; ++i)
            for(int j = 0; j <= i - t; ++j)
                f[x][i] = max(f[x][i], f[x * 2][j] + f[x * 2 + 1][i - t - j]);
    }
}
int main() {
    cin >> n, --n, dfs(1), cout << f[1][n] << endl;
    return 0;
}
posted @ 2023-07-20 12:07  AgrumeStly  阅读(21)  评论(0编辑  收藏  举报