狀態設的很神,設 \(dp[i][j][k]\) 表示以 \(i\) 為根的子樹內, \(i\) 不建伐木場,子樹內有 \(j\) 個伐木場的答案,且往上最近的建了伐木場的祖先為 \(k\) 。設 \(dp2[i][j][k]\) 表示以 \(i\) 為根的子樹內, \(i\) 建伐木場,子樹內有 \(j\) 個伐木場的答案(不包括 \(i\) ),且往上最近的建了伐木場的祖先為 \(k\) 的答案
轉移直接轉,注意要倒序,不然之前的值被更新了,後面的就更新會變多,然後實現有一些細節
#include<bits/stdc++.h>
#define vd void
const int inf=2000000000;
int gi(){
char c;int x=0,f=0;
while(!isdigit(c=getchar()))f|=(c=='-');
while(isdigit(c))x=(x*10)+(c^48),c=getchar();
return f?-x:x;
}
template<class T>vd cxk(T&a,T b){a=a>b?a:b;}
template<class T>vd cnk(T&a,T b){a=a<b?a:b;}
struct E{
int nxt,to,w;
}e[105];
int cnt,hd[105];
int n,K,W[105],dis[105];
int dp[105][55][105],dp2[105][55][105]; //在u放,在u不放
int top,st[105];
vd add(int from,int to,int w){e[++cnt]={hd[from],to,w},hd[from]=cnt;}
vd dfs(int u){
st[++top]=u;
for(int t=hd[u];t;t=e[t].nxt){
int v=e[t].to,w=e[t].w;
dis[v]=dis[u]+w;dfs(v);
for(int qwq=1;qwq<=top;qwq++){
int k=st[qwq];
for(int i=K;i>=0;i--){
dp[u][i][k]+=dp[v][0][k],dp2[u][i][k]+=dp[v][0][u];
for(int j=0;j<=i;j++)
cnk(dp[u][i][k],dp[u][i-j][k]+dp[v][j][k]),cnk(dp2[u][i][k],dp2[u][i-j][k]+dp[v][j][u]);
}
}
}
for(int i=1;i<=top;i++){
int k=st[i];
for(int j=0;j<=K;j++)dp[u][j][k]=std::min(j||j==K?dp2[u][j-1][k]:inf,dp[u][j][k]+W[u]*(dis[u]-dis[k]));
//j-1是因為之前dp2裡面沒把u算進去
}
--top;
}
int main(){
n=gi(),K=gi();
for(int i=1;i<=n;i++){
int v,d;W[i]=gi();v=gi(),d=gi();
add(v,i,d);
}
dfs(0);
printf("%d\n",dp[0][K][0]);
return 0;
}