hdu 4003 Find Metal Mineral(树形DP)

来源:互联网 发布:淘宝服装店女装 编辑:程序博客网 时间:2024/04/30 16:49
     题意:给一棵树,从一个节点到下一个节点需要消耗一定的能量,以最小的能量消耗遍历这棵树。
      解法:首先试着思考一个问题:如果有多个机器人去往一个子节点,那有多少机器人需要回来?
      假如从father节点去遍历它的子节点son,首先要明白一点如果所有的机器人都要回来,那还不如只让一个机器人去。因为在遍历一个节点的时候,如果要返回的话,那么需要消耗的能量是双倍的。比如说对于son节点,如果只去一个机器人,消耗是2*father->son->cost(注意需要返回);如果让两个机器人去,那么消耗就是4*father->son->cost。
       那么多个机器人能给我们带来的好处是什么呢?
       就是在不返回的情况下,某些节点我们可以少走一次(去了就不要回来了)。同样以a和b遍历son这个子树为例子,如果son还有两个son分别是son1和son2。如果我们让一个机器人去,消耗就是2*son->son1->cost+son->son2->cost;如果让两个机器人去,消耗就是son->son1->cost+son->son2->cost。可以看出来,在任何情况下,返回多个(超过一个)机器人都是不明智的,因为在这样情况下,多出的机器人并不能节省任何能源,但是在这个机器人返回son的父节点father节点的时候还需要多消耗一个father->son->cost。

       这时候我们再考虑遍历一个子树son所消耗的能量,在去的机器人数量不同的情况下消耗的能量是不同的。这里可以参考《背包九讲》中泛化背包的思想。至于优化方案的时候跟之前做的1011和1561不太一样,这个不只是只能取一个,还是必须去一个。开始的时候想的是把dp数组初始化为-1来解决这个问题,但是这样写起来代码需要多加一些判断还要对叶节点进行特别处理,看起来不是很美观。再然后看到了这种直接给每一个包一个初始值,然后再进行计算的方式,这样看起来代码漂亮得多。

       附上大神的博客:点击打开链接

#include<stdio.h>#include<string.h>#define N 10005#define K 12int dp[N][K],ans[K][N];int n,s,k;struct node{    int son;    int next;    int data;}Edge[N*2];int cnt,head[N],vis[N];int Min(int x,int y){    if(x<y) return x;    else return y;}void AddEdge(int x,int y,int k){    Edge[cnt].data=k;Edge[cnt].son=y;Edge[cnt].next=head[x];head[x]=cnt++;    Edge[cnt].data=k;Edge[cnt].son=x;Edge[cnt].next=head[y];head[y]=cnt++;    return ;}void dfs(int father){    int u=head[father];    vis[father]=1;    for(int i=u;i!=-1;i=Edge[i].next)    {        int son=Edge[i].son;        if(vis[son]) continue;        dfs(son);        for(int t=k;t>=1;t--)        {            dp[father][t]=Min(dp[father][t-1]+dp[son][1]+Edge[i].data,dp[father][t]+dp[son][0]+2*Edge[i].data);            for(int j=1;j<=t;j++)                dp[father][t]=Min(dp[father][t],dp[father][t-j]+dp[son][j]+j*Edge[i].data);        }        dp[father][0]+=dp[son][0]+2*Edge[i].data;    }    return ;}int main(){    while(scanf("%d%d%d",&n,&s,&k)!=EOF)    {        memset(head,-1,sizeof(head));        cnt=0;        for(int i=1;i<n;i++)        {            int x,y,k;            scanf("%d%d%d",&x,&y,&k);            AddEdge(x,y,k);        }        memset(vis,0,sizeof(vis));        memset(dp,0,sizeof(dp));        dfs(s);        printf("%d\n",dp[s][k]);    }    return 0;}


0 0