搜集钻石
Description
蒜国有 n 座城市,编号从 1 到 n,城市间有 n−1 条道路,且保证任意两座城市之间是连通的。每一座城市有一定数量的钻石。
蒜头君想在蒜国搜集钻石。他从城市 1 出发,每天他可以通过城市之间道路开车到另外的城市。当蒜头第一次到一个城市的时候,他可以搜集完这个城市的所有钻石,如果他后面再来到这个城市,就没有砖石可以收集了。
蒜头君只有 K 天时间,请你帮算蒜头君计算他最多可以搜集多少钻石。
Input
第一行输入三个整数 n(1≤n≤100),K(0≤k≤200),表示一共有 n 座城市,蒜头君有 K 天时间。
接下里一行输入 n 个用空格隔开的整数,表示每个城市的钻石数量。每个城市的钻石数量不大于 1000。
接下来输入 n−1 行,每行输入两个整数 a(1≤a≤n),b(1≤b≤n),表示城市 a和城市 b 之间存在一条双向道路。
Output
输出一行一个整数表示蒜头君最大能获取的钻石数量。
Sample Input:
3 2
3 8 3
1 3
3 2
Output:
14
树形DP。
dp[i][j][0/1]:现节点为i,用了j天,0表示不回去,1表示回去。
#include <bits/stdc++.h>
#define For(i, j, k) for(int i = (j); i <= (k); i++)
using namespace std;
int n;
const int MAXN = 205;
struct Edge {
int v, nxt;
}e[MAXN << 1];
int lst[MAXN], tott = 0, m;
inline void addedge(int u, int v) {
e[++tott].v = v;
e[tott].nxt = lst[u];
lst[u] = tott;
}
int val[MAXN];
int dp[MAXN][MAXN][2];//DP[i][j][k]:第i个点,已经用了j天,1回来,0不回
void dfs(int u, int fa) {
For(i, 0, m) dp[u][i][1] = dp[u][i][0] = val[u]; //先把dp[u][i]初始值赋为val
for(int i = lst[u]; i; i = e[i].nxt) { //枚举u的每一条出边
int v = e[i].v;
if(v != fa) {
dfs(v, u);
for(int j = m; j >= 1; j--) { //当前点现在有几天
For(e, 0, j-1) {//当前点推来时走了e天
if(j - e >= 2) {
dp[u][j][1] = max(dp[u][j][1], dp[u][e][1] + dp[v][j-e-2][1]); // 如果当前这个点回去,则其子节点也必须回去
dp[u][j][0] = max(dp[u][j][0], dp[u][e][0] + dp[v][j-e-2][1]);// 如果当前点不回去,但子树v全部回去
}
dp[u][j][0] = max(dp[u][j][0], dp[u][e][1] + dp[v][j-e-1][0]);//当前点出去就不回来了 ,剩下的e天回去
}
}
}
}
}
int main()
{
cin >> n >> m;
For(i, 1, n) {
scanf("%d", &val[i]);
}
For(i, 1, n-1) {
int u, v;
scanf("%d %d", &u, &v);
addedge(u, v);
addedge(v, u);
}
dfs(1, -1);
cout << dp[1][m][0] << endl;
return 0;
}