bzoj 1812 [IOI2005] riv (樹形dp,樹上揹包)
阿新 • • 發佈:2018-12-13
這道題比較有難度。。。首先可以想到f[i][k]表示i點的子樹中用了k個伐木場。但是這顯然沒法轉移。那麼我們加上一維,
f[i][j][k]表示i點的子樹內用了k個伐木場並且上一個用的位置為j,而當前點可選可不選(看第二維的狀態),之後用子樹更新即可
合併的時候和更新的時候要特殊考慮i==j的情況。
列舉這個點其他子樹內用了w個,這個子樹為k-w
當不選擇這個點時f[i][j][k]=min(f[to][j][k-w]+f[i][j][w])
選擇這個點時為dp[i][i][k]=min(dp[to][i][k-w]+dp[i][i]w])
最後合併
f[i][j][k]+=val[i]*dis[i][j]
f[i][j][k]=min(f[i][j][k],f[i][i][k])
#include<cstdio> #include<cmath> #include<algorithm> #include<cstring> using namespace std; int n,m,tot=0x3f3f3f3f,fa[205],dis[205][205],f[205][205][55],val[205]; struct node { int to; int nxt; }edge[40005]; int head[205]; int cnt=1; void init() { memset(head,-1,sizeof(head)); memset(fa,-1,sizeof(fa)); } void add(int from,int to) { edge[cnt].to=to; edge[cnt].nxt=head[from]; head[from]=cnt++; } void dfs(int u) { int la=u; for(int i=fa[u];i!=-1;i=fa[i]) { dis[u][i]=dis[u][la]+dis[la][i]; la=i; } if(head[u]==-1) { for(int i=fa[u];i!=-1;i=fa[i]) { f[u][i][0]=dis[u][i]*val[u]; } return; } if(u)f[u][u][0]=0x3f3f3f3f; for(int i=head[u];i!=-1;i=edge[i].nxt) { int to=edge[i].to; dfs(to); for(int j=fa[u];j!=-1;j=fa[j]) { for(int k=m;k>=0;k--) { int tmp=0x3f3f3f3f; for(int w=0;w<=k;w++) { tmp=min(tmp,f[u][j][w]+f[to][j][k-w]); } f[u][j][k]=tmp; } } for(int k=m;k>=0;k--) { int tmp=0x3f3f3f3f; for(int w=(u!=0);w<=k;w++) { tmp=min(tmp,f[u][u][w]+f[to][u][k-w]); } f[u][u][k]=tmp; } } for(int i=fa[u];i!=-1;i=fa[i]) { for(int j=0;j<=m;j++) { f[u][i][j]+=val[u]*dis[u][i]; f[u][i][j]=min(f[u][i][j],f[u][u][j]); } } } int main() { scanf("%d%d",&n,&m); init(); for(int i=1;i<=n;i++) { scanf("%d",&val[i]); int a; scanf("%d%d",&fa[i],&a); add(fa[i],i); dis[i][fa[i]]=a; } dfs(0); printf("%d",f[0][0][m]); return 0; }