[bzoj1812][Ioi2005]riv

树dp还是一如既往的烂呢。普通树转成左二子右兄弟树还是第一次用,思想完全没变吧,就是好写了。


 

题意就是给一棵树,树的点上有物品,边有长度,物品只能往祖先送,要修建$k$个接收的地方。令代价最小。(日了狗了沉船之后说话都说不清楚。还是看原题吧)$n \leq 100 , k \leq 50$//upd:后来出货了

令$f[i][j][k]$表示$i$的子树及其左侧所有兄弟的子树里,修建$j$个伐木场,并且$i$最近一个修建伐木场的祖先为$k$,最小代价。

分两种情况转移:

1.在$i$处修建伐木场,那么可以从$f[son][x][i]+f[bro][j-x-1][k]$转移过来。因为在这里修建不能影响兄弟,会影响儿子。

2.不在$i$处修建伐木场,那么可以从$f[son][x][k]+f[bro][j-x][k]+w[i] \times dis(i,k)$转移过来。

主要还是利用树形背包的思想。左儿子右兄弟确实使代码简介许多。

 

#include<bits/stdc++.h>
using namespace std;
const int N=101;
inline int read(){
    int r=0,c=getchar();
    while(!isdigit(c))c=getchar();
    while(isdigit(c))
    r=r*10+c-'0',c=getchar();
    return r;
}
struct Edge{
    int to,nxt,w;
}e[N*2];
int head[N],cnt=1;
int ls[N],rs[N],d[N];
void add(int u,int v,int w){
    rs[v]=e[head[u]].to;
    e[cnt]=(Edge){v,head[u],w};
    head[u]=cnt++;
}
void getdep(int u,int dep){
    d[u]=dep;
    for(int i=head[u];i;i=e[i].nxt)
    getdep(e[i].to,dep+e[i].w);
}
int n,k;
int w[N],f[N][N][N];
int dp(int u,int x,int v){
    if(!~u)return 0;
    if(~f[u][x][v])return f[u][x][v];
    f[u][x][v]=2e9;
    int tmp=w[u]*(d[u]-d[v]);
    for(int i=0;i<=x;i++)
    f[u][x][v]=min(f[u][x][v],dp(ls[u],i,v)+dp(rs[u],x-i,v)+tmp);
    for(int i=0;i<x;i++)
    f[u][x][v]=min(f[u][x][v],dp(ls[u],i,u)+dp(rs[u],x-i-1,v));
    return f[u][x][v];
}
void init(){
    memset(f,-1,sizeof f);
    e[0].to=-1;rs[0]=-1;
    n=read(),k=read();
    for(int i=1;i<=n;i++){
        w[i]=read();
        int v=read(),w=read();
        add(v,i,w);
    }
    for(int i=0;i<=n;i++)
    ls[i]=e[head[i]].to;
}
void solve(){
    printf("%d\n",dp(0,k,0));
}
int main(){
    init();
    getdep(0,0);
    solve();
}
/*
4 2
1 0 1
1 1 10
10 2 5
1 2 3
*/

 

posted @ 2018-02-16 12:47  orzzz  阅读(173)  评论(0编辑  收藏  举报