HDU 4003 Find Metal Mineral 树上分组背包

来源:互联网 发布:高效程序员的狂暴之路 编辑:程序博客网 时间:2024/06/03 06:24

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=4003
题意:一颗有根树N个节点的树和K个机器人,问把所有点都走到的最小花费

树上分组背包问题

这道题有一个要求就是这个机器人走完一颗树之后,还是可以回到树的根节点的,但也可以又机器人走这颗子树后不回到树根。
但是我们只要仔细的分析一下就可以看出来,假如一颗子树存在又机器人进去后又走回到树根,那么这个树只可能只放了一个机器人,且这个机器人走完这颗子树后回到树根。

情况1.有p(p > 1 )个机器人走这颗树,且它们都回到了树根。此时的花费是:子树边权和加上p个机器人回到根节点的路径和。此时假如只有一个机器人走这颗子树,花费为:子树边权和加上一条回到树根的路劲和。所以一个机器人的花费一定小于p个机器人的花费

情况2.有p(p > 1 )个机器人走这颗树,只有一个机器人回到了根节点,此时的花费和一个机器人走又回来是一样的:子树边权和加上一条回到树根的路径和。所以我们可以去掉剩下p - 1个没有回到根节点的机器人,这样不会更差还能节省机器人。

情况3.有p(p > 1) 个机器人走这颗子树,有q(1 < q < p)个机器人回到树根,那么同样花费为树上边权和 加上 p个回到树根的路径和,用一个机器人代替能得到更优的答案。

以上,在最优状态关于有机器人会回到树根的时候,只可能是1个机器人进入然后回到这个树根。

于是我们用这里写图片描述
i = 0,对于子树rt有1个机器人进入然后又回到树根
i > 0,对于子树rt又i个机器人进入没有回到树根

对于每一颗子树,都做一个分组背包,每一个儿子的不同情况分到一组。背包容量为机器人个数k,物品价值为dp[son][j],代价为[j]。

i == 0 :这里写图片描述
i>0:这里写图片描述
代码:

#include <bits/stdc++.h>#define sf scanf#define pf printfusing namespace std;const int maxn = 10000 + 5,maxm = 10 + 5,INF = 0x3f3f3f3f;int n,s,k;struct edge{    int v,c,pre;}edges[maxn * 2];int head[maxn],tot;void init_edge(){    memset(head,-1,sizeof head),tot = 0;}void Insert_Edge(int u,int v,int c){    edges[tot].c = c;    edges[tot].v = v;    edges[tot].pre = head[u];    head[u] = tot++;}int dp[maxn][maxm];/** dp[i][0] 表示第i个子树中放入了1个机器人 且这个机器人走完这个树后 回到节点i */void DFS(int rt,int fa){    memset(dp[rt],0,sizeof dp[rt]);    for(int i = head[rt];~i;i = edges[i].pre){  //背包分组        int v = edges[i].v,c = edges[i].c;        if(v == fa) continue;        DFS(v,rt);        //分组背包        //枚举容量        for(int j = k;j >= 0;--j){            dp[rt][j] += dp[v][0] + 2 * c;            //枚举物品            for(int p = 1;p <= k;++p){                if(p > j) break;                dp[rt][j] = min(dp[rt][j],dp[rt][j - p] + dp[v][p] + c * p);            }        }    }}int main(){    while(~sf("%d %d %d",&n,&s,&k)){        init_edge();        for(int i = 1;i < n;++i){            int u,v,c;sf("%d %d %d",&u,&v,&c);            Insert_Edge(u,v,c);            Insert_Edge(v,u,c);        }        DFS(s,0);        int ans = INF;        for(int i = 0;i <= k;++i) ans = min(ans,dp[s][i]);        pf("%d\n",ans);    }    return 0;}
0 0
原创粉丝点击