solution
开题显然是个树形 dp,只不过在树形 dp 上又增加了背包问题。
我们不妨将每个走廊看成一个点,把交叉口看成边(当然也可以把交叉口看成点,不过写起来麻烦一些),于是就转化为了一棵二叉树。
我们设 \(f_{i,j}\) 表示以 \(i\) 为根的子树内,花费了不超过 \(j\) 时间,能拿到的最大价值。
考虑转移,发现输入是按深度给的,我们不妨一边 dfs 一边输入一边做转移。
对于当前节点(走廊) \(x\),我们两种情况分别讨论:
- 走廊的尽头是展览厅。
- 走廊的尽头是分叉口。
对于第一种,这里是一个显然的 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}\)
坑点
- 注意每一条走廊我们不光需要走进去,还需要走出来回到出口,于是实际上每一条走廊需要走两遍,我们在输入的时候将该走廊需要花费的时间 \(\times 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;
}
标签:int,题解,dfs,times,花费,偷天换日,走廊,我们,P3360
From: https://www.cnblogs.com/agrumestly/p/17567976.html