题目描述
某国有 n座城市,编号从 1到 n,城市间有 n−1 条道路,且保证任意两座城市之间是连通的。每一座城
市有一定数量的钻石。小明想在该国搜集钻石。他从城市 1出发,每天他可以通过城市之间道路开车到另
外的城市。当小明头第一次到一个城市的时候,他可以搜集完这个城市的所有钻石,如果他后面再来到这
个城市,就没有砖石可以收集了。 小明只有 K天时间,请你帮算小明计算他最多可以搜集多少钻石。
输入格式: 第一行输入三个整数 n(1≤n≤100),K(0≤k≤200),表示一共有 n 座城市,小明有 K 天时间。 接下里一行输入 n 个用空格隔开的整数,表示每个城市的钻石数量。每个城市的钻石数量不大于 1000。接下来输入 n−1 行,每行输入两个整数 a(1≤a≤n),b(1≤b≤n),表示城市 a和城市 b之间存在一条双向道路。
输出格式: 输出一行一个整数表示小明最大能获取的钻石数量。
样例输入1
3 2
3 8 3
1 3
3 2
样例输出1
14
样例输入2
6 2
5 9 8 4 9 2
1 6
6 2
2 5
5 3
5 4
样例输出2
16
初步分析
这道题是背包问题但既不是01背包也不是多重背包,但总的思想还是分解容量(天数)来更新最大值,不过是用新的子树来更新罢了。
我们设dp[node][j][0],dp[node][j][1]分别表示结点node除法经过j天未回来和回来搜索到钻石的最大值。
那么该怎么更新呢。
1,要回来:dp[node][j][1],先用e天从node除法再回来,(花一天到更新的子节点v)再花j-e-2天从新增结点v出发再回来,最后回到结点node.用这条路上收集到的钻石来更新最大值。所以dp[node][j][1]=max(dp[node][j][1],dp[node][e][1]+dp[v][j-e-2][1])由于是v新增结点所以这两部分不会有路径重复所以能直接相加。
2,不回来:dp[node][j][0],这种状态可以分为两种情况。(其实也可以理解成三种情况不过等会讲)。
A:先到新增子结点v,再从新增子节点v出发花j-e-2天回来,再从node花e天不回来。
B:从node出发花e天回来,再到新增结点然后话j-e-1天不回来。
所以dp[node][j][0]=max(dp[node][j][0],dp[v][j-e-2][1]+dp[node][e][0])
dp[node][j][0]=max(dp[node][j][0],dp[node][e][1]+dp[v][j-e-1][0])
聪明的你有没有想到为什么第二个式子不考虑+dp[v][j-e-1][1]的情况呢。其实很容易能想到(dp[v][j-e-1][0]>=dp[v][j-e-1][1])。(PS:说实话我也是看了别人的代码才想到的,自己做的时候写的状态方程根本实行不了。)
那我们刚才说的第三种情况你知道是什么嘛?其实就是新增子树或者原来的node数根本不经过的情况,也就是不回来的情况。不过当j-e-2=0,j-e-1=0,e=0的时候就已经把这种情况考虑进去了。
还要注意的一点就是j的值应该从大到小,因为0=<e<=j-1,保证dp[node][e][1]的值一直是原来未新增子节点之前的最大值。有点类似于01背包的一维空间压缩。
解析得差不多啦,这道题确实困了我好久。网上看的几篇大佬的解析感觉不是讲得很清楚。希望大家能看懂我这个啰嗦的版本。自己也要动脑筋哦
直接上代码
#include <iostream>
#include <string.h>
using namespace std;
const int MAX_N=110;
const int MAX_M=220;
const int MAX_K=220;
int n,k;
int dp[MAX_N][MAX_K][2];
int w[MAX_N];
int ans=0;
int head[MAX_N];
struct edge{
int v;
int next;
}eid[MAX_M];
void insert(int u,int v){
eid[ans].v=v;
eid[ans].next=head[u];
head[u]=ans++;
}
void dfs(int node,int father){
for(int i=0;i<=k;++i){
dp[node][i][0]=w[node];
dp[node][i][1]=w[node];
}
for(int i=head[node];i!=-1;i=eid[i].next){
int v=eid[i].v;
if(v==father) continue;
dfs(v,node);
for(int j=k;j>=1;--j){
for(int e=0;e<=j-1;++e){
if(j-e>=2){
dp[node][j][1]=max(dp[node][j][1],dp[node][e][1]+dp[v][j-e-2][1]);
dp[node][j][0]=max(dp[node][j][0],dp[v][j-e-2][1]+dp[node][e][0]);
}
dp[node][j][0]=max(dp[node][j][0],dp[node][e][1]+dp[v][j-e-1][0]);
}
}
}
}
int main() {
memset(head,-1, sizeof(head));
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i){
scanf("%d",&w[i]);
}
int x,y;
for(int i=1;i<=n-1;++i){
scanf("%d%d",&x,&y);
insert(x,y);
insert(y,x);
}
dfs(1,-1);
cout<<dp[1][k][0]<<endl;
return 0;
}