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 */