树上背包,更复杂的状态设计。

Table of Contents

  1. Description
  2. Solution

Description

自从 Y 君退役之后,她就迷上了吃鸡,于是她决定出一道吃鸡的题。

Y 君将地图上的所有地点标号为 1 到 n,地图中有 n − 1 条双向道路连接这些点,通过一条双向道路需要一定时间,保证从任意一个点可以通过道路到达地图上的所有点。 有些点上可能有资源,Y 君到达一个有资源的点后,可以选择获取资源来使自己的武力值增 加 wi,也可以选择不获取资源。如果 Y 君获取了一个点上的资源,这个点上的资源就会消失,获 取资源不需要时间。

有些点上可能有敌人,Y 君到达一个有敌人的点后,必须花费 ti 秒伏地与敌人周旋,并最终将敌人消灭。如果 Y 君消灭了一个点上的敌人,这个点上的敌人就会消失。Y 君不能无视敌人继 续前进,因为这样会被敌人攻击。

如果一个点上既有资源又有敌人,Y 君必须先消灭敌人之后才能获取资源,否则就会被敌人突袭。

游戏开始时,Y 君可以空降到任意一个点上,接下来,她有 T 秒进行行动,T 秒后她就必须前往中心区域送快递。Y 君希望她前往中心区域送快递时,武力值尽可能大,请你帮助 Y 君设计路线,以满足她的要求。你只需输出 T 秒后 Y 君的武力值。

Solution

几天前Bouvardia做了一道类似的树上背包,所不同的是,这道题可以在任意节点开始,任意节点结束,所以我们需要扩展状态,分别是:

  1. 从当前节点出发,回到当前节点,耗费时间T。
  2. 从当前节点出发,不回到当前节点,耗费时间T。
  3. 从当前节点子树某一结点出发,到达当前节点子树某一结点。

这样全局统计最大值就好了喵~

状态讨论:

对于枚举到儿子 $y$ , 我们有:

  • 儿子回到儿子 + 之前的儿子回到自己 > 回到自己.
  • 儿子不回到儿子 + 之前的儿子回到自己 > 不回到自己.
  • 儿子回到儿子 + 之前的儿子不会到自己 > 不会到自己.
  • 儿子回到儿子 + 之前的儿子乱走 > 自己的乱走.
  • 之前的儿子回到自己 + 儿子乱走 > 自己的乱走.
  • 之前的儿子不回到儿子 + 儿子不回到儿子 > 自己的乱走.

记得每种状态转移相应的边长代价和自己的强制代价的说.

有一个细节: 转移的时候经常需要互相调用状态,从而可能出现转移的来源错误的情况, 可以拷贝一份已经完成的数组呢.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>

using namespace std;

const int maxn = 305;

struct edge
{
int to, nxt, v;
}e[maxn << 1];
int f[maxn][maxn], g[maxn][maxn], h[maxn][maxn];
int n, lnk[maxn], ptr, T, ans;
int t[maxn], w[maxn];

inline void add(int bgn, int end, int val)
{
e[++ptr] = (edge){end, lnk[bgn], val};
lnk[bgn] = ptr;
}
void dfs(int i, int fa)
{
int cf[maxn], cg[maxn], ch[maxn];
for(int j = t[i]; j <= T; ++j)
f[i][j] = w[i],
g[i][j] = w[i],
h[i][j] = w[i];
for(int p = lnk[i]; p; p = e[p].nxt)
{
int y = e[p].to;
if(y == fa) continue;
dfs(y, i);
for(int j = 0; j <= T; ++j)
cf[j] = f[i][j], cg[j] = g[i][j], ch[j] = h[i][j];
for(int j = 0; j <= T; ++j)
{
for(int k = 0; k <= T; ++k)
{
if(j - k - 2 * e[p].v >= 0)
h[i][j] = max(h[i][j], ch[j - k - 2 * e[p].v] + f[y][k]),
h[i][j] = max(h[i][j], cf[j - k - 2 * e[p].v] + h[y][k]);
if(j - k - e[p].v >= 0)
h[i][j] = max(h[i][j], cg[j - k - e[p].v] + g[y][k]);
}
}
for(int j = T; j >= 0; --j)
{
for(int k = 0; k <= T; ++k)
{
if(j - k - 2 * e[p].v >= 0)
f[i][j] = max(f[i][j], cf[j - k - 2 * e[p].v] + f[y][k]);
}
}
for(int j = T; j >= 0; --j)
{
for(int k = 0; k <= T; ++k)
{
if(j - k - 2 * e[p].v >= 0)
g[i][j] = max(g[i][j], cg[j - k - 2 * e[p].v] + f[y][k]);
}
}
for(int j = T; j >= 0; --j)
{
for(int k = 0; k <= T; ++k)
{
if(j - k - e[p].v >= 0)
g[i][j] = max(g[i][j], cf[j - k - e[p].v] + g[y][k]);
}
}
}
ans = max(max(ans, f[i][T]), max(g[i][T], h[i][T]));
}

int main()
{
freopen("toyuq.in", "r", stdin);
freopen("toyuq.out", "w", stdout);
scanf("%d%d", &n, &T);
for(int i = 1; i <= n; ++i) scanf("%d", &w[i]);
for(int i = 1; i <= n; ++i) scanf("%d", &t[i]);
register int x, y, z;
for(int i = 1; i < n; ++i)
{
scanf("%d%d%d", &x, &y, &z);
add(x, y, z); add(y, x, z);
}
memset(f, 0xcf, sizeof f);
memset(g, 0xcf, sizeof g);
memset(h, 0xcf, sizeof h);
dfs(1, 0);
printf("%d\n", ans);
fclose(stdin);
fclose(stdout);
return 0;
}