第 45 屆國際大學生程式設計競賽(ICPC)亞洲區域賽(南京)M Monster Hunter —— 樹形DP
阿新 • • 發佈:2020-12-22
題意:
現在有一棵根為1的樹,你要將所有點消除,消除一個點i首先需要消除它的父親,然後消除i的代價是
也就是hp[i]+還未被消除的所有兒子的hp和
你有一種魔法可以無視所有規則消除掉一個點。
問你這個魔法的使用次數為i(0<=i<=n)次時,最少需要的代價是多少。
題解:
很明顯是樹形DP,但是列舉兒子的狀態轉移的話,時間複雜度會變成
n
3
n^3
n3,所以需要使用上下界優化
那麼消除當前數的時候,需要加上所有未被消除的兒子的值,因此DP需要三維
dp[i][j][k]表示到了第i個點,使用了j次魔法,當前點是否一開始就使用魔法消除時最小的代價。 那麼邊界值:
dp[x][1][0]=0
dp[x][0][1]=a[x]
然後轉移:
首先是這個點在一開始就被消除
dp[x][i+j][0]=min(dp[x][i+j][0],dp[x][i][0]+min(dp[ne][j][0],dp[ne][j][1]));
還有一種就是這個點按照常規的消除方法,它需要加上兒子的代價
dp[x][i+j][0]=min(dp[x][i+j][0],dp[x][i][0]+min(dp[ne][j][0],dp[ne][j][1]+a[ne]));
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e3+5;
ll dp[N][N][2],a[N];
const ll inf=1e18;
vector<int>son[N];
int siz[N];
ll tmp[N];
void dfs(int x){
siz[x]=1;
dp[x][0][0]=inf,dp[x][1][0]=0;
//dp[x][0][1]=a[x];
int f=0;
for(int ne:son[x]){
dfs(ne);
for(int i=0;i<=siz[x]+siz[ne];i++)tmp[i]= inf;
for(int i=0;i<=siz[x];i++){
if(dp[x][i][0]==inf)continue;
for(int j=0;j<=siz[ne];j++)
tmp[i+j]=min(tmp[i+j],dp[x][i][0]+min(dp[ne][j][0],dp[ne][j][1]));
}
siz[x]+=siz[ne];
for(int i=0;i<=siz[x];i++)dp[x][i][0]=tmp[i];
}
//for(int i=1;i<=siz[x];i++)dp[x][i][1]=dp[x][i][0]+a[x];
//int s=vec[x].size();
ll sum=0,v=0;
dp[x][0][1]=a[x];
siz[x]=1;
for(int ne:son[x]){
for(int i=0;i<=siz[x]+siz[ne];i++)tmp[i]=inf;
for(int i=0;i<=siz[x];i++){
if(dp[x][i][1]>=inf)continue;
for(int j=0;j<=siz[ne];j++)
tmp[i+j]=min(tmp[i+j],dp[x][i][1]+min(dp[ne][j][0],dp[ne][j][1]+a[ne]));
}
siz[x]+=siz[ne];
for(int i=0;i<=siz[x];i++)dp[x][i][1]=tmp[i];
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
int n,x;
scanf("%d",&n);
for(int i=1;i<=n;i++)son[i].clear(),siz[i]=0;
for(int i=1;i<=n;i++)
for(int j=0;j<=n;j++)
for(int k=0;k<=1;k++)
dp[i][j][k]=inf;
for(int i=2;i<=n;i++)
scanf("%d",&x),son[x].push_back(i);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
dfs(1);
for(int i=0;i<=n;i++)
printf("%lld%c",min(dp[1][i][0],dp[1][i][1])," \n"[i==n]);
}
return 0;
}