大致题意
给一颗(n)个节点的树,根节点是一个伐木场,每个节点都有一个村庄,每个村庄的每根木头运送到其父亲的代价是(d_i),现在要建立(k)个伐木场,每个村庄有(w_i)根木头要沿树边运到最近的伐木场,求最小代价
分析
树形(dp)
一开始比较容易想到的是设(f(i,j))表示在(i)的子树中建立(j)个伐木场所花费的最小代价
发现无法得知一个节点的上一个伐木场在哪,即无法计算代价,考虑在状态中记录上一个伐木场的位置
设(f(i,j,k))表示上一个伐木场的位置是(j),(i)的子树中建立(j)个伐木场所花费的代价
有显然的转移:
亿点细节:
-
记得删除不合法的状态,详细见代码
-
枚举(j)时要先判断(j)是否在(u)的祖先中
(code)
#include<bits/stdc++.h>
using namespace std;
#define inf (1<<30)
const int MAXN = 110;
int head[MAXN<<1];
struct e{
int v,w,next;
}edge[MAXN<<1];
int cnt = 0;
int n,K;
int c[MAXN],f[MAXN][MAXN][MAXN];
int is_fa[MAXN];
int dis[MAXN];
void add(int u,int v,int w){
edge[++cnt].v = v;
edge[cnt].next = head[u];
edge[cnt].w = w;
head[u] = cnt;
}
void dfs(int u){
is_fa[u] = true;
for(int i=0;i<=n;i++) for(int j=0;j<=K;j++){
if(!is_fa[i]) f[u][i][j] = inf;//除去不合法状态
else if(i==0||u!=i) f[u][i][j] = (dis[u] - dis[i])*c[u];
else if(j!=0) f[u][i][j] = 0;
else f[u][i][j] = inf;//显然在u=i且j=0时状态不合法
}
for(int i=head[u];i;i=edge[i].next){
int v = edge[i].v;
dis[v] = edge[i].w+dis[u];
dfs(v);
for(int j=0;j<=n;j++){
if(!is_fa[j]) continue;//判断是否为祖先
for(int k=K;k>=0;k--){
f[u][j][k]+=f[v][j][0];
for(int s=1;s<=k;s++){
f[u][j][k] = min(f[u][j][k],f[u][j][k-s]+min(f[v][v][s],f[v][j][s]));
}
}
}
}
is_fa[u] = false;
}
int main(){
cin>>n>>K;
for(int i=1;i<=n;i++){
int u,w;
cin>>c[i]>>u>>w;
add(u,i,w);
dis[i] = w;
}
dfs(0);
cout<<f[0][0][K];
}