D、杀树
解题思路
树形背包,我是通过 保存的树
码量小,符合我这种懒人,在这个基础上。
通过题目给的范围小于等于5000可以满足平方级复杂度,开一个二维数组去维护。 表示以i为根的子树,向下传j个长度的最小值,那么通过dfs从叶子往根节点推。
首先,初始状态表示去掉了自己,还要保证子树剩余的满足题目意思。就把子树中
中拿出最小的累加进这个根节点的
的花费。
其余的,根据
for(int i=1; i<m;++i){ //自己满足最长小于等于i,不删除连接子节点的链
for(int j=0; j<m; ++j){ // 孩子为根节点的子树最长链满足小于等于j
if(i+j<m)
// 这个时候整个u最长就是子树剩余+连接根节点 or 指定根节点剩余的最长链 取较大者
// 对应花费累加求最小
f[u][max(i,j+1)]=min(f[u][max(i,j+1)],tmp[i]+f[it][j]);
else
break;
}
}
https://ac.nowcoder.com/acm/contest/view-submission?submissionId=43642924
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read(){
int s = 0, w = 1; char ch = getchar();
while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); }
while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
return s * w;
}
const int N = 5e3+7;
const int INF = 0x3f3f3f3f;
int a[N],f[N][N],tmp[N];
// f[i][j] 表示以i为根的子树,最长链小于等于j的最小值
vector<int> e[N];
int n,m;
void dfs(int u,int fa){
f[u][0]=a[u]; //吧自己删除
for(auto it : e[u]){
if(it==fa) continue;
dfs(it,u);
f[u][0]+=*min_element(f[it],f[it]+m); //删除连接子节点的链,还要满足子树中符合题目条件
for(int i=1; i<m; ++i)
tmp[i]=f[u][i],f[u][i]=INF; //可能多次更新,下面要求最小值,控制成无穷大
for(int i=1; i<m;++i){ //自己满足最长小于等于i,不删除连接子节点的链
for(int j=0; j<m; ++j){ // 孩子为根节点的子树最长链满足小于等于j
if(i+j<m)
f[u][max(i,j+1)]=min(f[u][max(i,j+1)],tmp[i]+f[it][j]);
else
break;
}
}
}
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1;i<n;++i){
int u=read(),v=read();
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1,0);
printf("%d\n",*min_element(f[1],f[1]+m)); //min_element和max_element 返回区间最小或者最大
return 0;
} 
京公网安备 11010502036488号