https://www.luogu.com.cn/problem/CF1249F
这题看题解云里雾里的,我自认为我写的比较简单;
dp[x][i]表示以x为根,选中节点离x最近距离为i。(最小深度)
那么如何转移呢?
答案无非就是两种方式构成的,原来子树上就有,两棵树合并而成。于是就有了下面的式子
假设新树为x根,ans[x][min(i+j+1)] = max(dp[x][i] + dp[p][j] ) (p 为x的所有儿子)//表示参与合并
ans[x][i+1] = max(ans[p][i])//表示答案就在子树上
就是这样了,具体可以看代码。
#include<iostream>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 300+11;
typedef long long ll;
ll INF = 1e10;
vector<int>G[maxn];
ll dp[maxn][maxn];
int n,k;
ll list[maxn];
int dep[maxn];
void add(int x,int y){
G[x].push_back(y);
}
int dfs(int x,int fa){//感觉就是个背包
dep[x] = 1;
for(int s=0;s<G[x].size();s++){
int p = G[x][s];
if(p == fa) continue;
dfs(p,x);
for(int i=0;i< maxn-1 ;i++){
list[i] = dp[x][i];//分离出来避免重复计算
dp[x][i] = 0;
}
for(int i=0;i<maxn-1;i++){
for(int j=0;j< maxn-1;j++){
if(i + j + 1 > k) dp[x][min(i,j+1)] = max(dp[x][min(i,j+1)],list[i] + dp[p][j]);//距离要够远才能和一起
}
}
for(int j=0;j<maxn-1;j++){
dp[x][j+1] = max(dp[x][j+1],dp[p][j]);
}
dep[x] = max(dep[p]+1,dep[x]);
}
}
int main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>dp[i][0];
}
for(int i=1;i<n;i++){
int x,y;
cin>>x>>y;
add(x,y);
add(y,x);
}
dfs(1,-1);
ll ans = 0;
for(int i=0;i<=dep[1];i++){
if(dp[1][i] != INF) ans = max(ans,dp[1][i]);
}
cout<<ans<<endl;
return 0;
}
/*
14 4
6 4 1 2 3 3 5 2 2 7 3 6 6 1
13 9
3 12
10 11
14 13
6 7
5 2
8 12
10 14
2 14
6 8
1 9
4 13
8 13
输出14
*/