hdu4003 树形dp

来源:互联网 发布:2016年双十一数据统计 编辑:程序博客网 时间:2024/06/10 20:46

http://www.cnblogs.com/kuangbin/archive/2012/08/29/2661928.html


【转载】:

dp[i][j]表示对于以i结点为根结点的子树,放j个机器人所需要的权值和。 

当j=0时表示放了一个机器人下去,遍历完结点后又回到i结点了。状态转移方程类似背包 

如果最终的状态中以i为根结点的树中有j(j>0)个机器人,那么不可能有别的机器人r到了这棵树后又跑到别的树中去 

因为那样的话,一定会比j中的某一个到达i后跑与r相同的路径再回到i,再接着跑它的路径要差(多了一条i回去的边) 

这样的话,如果最后以i为根结点的树中没有机器人,那么只可能是派一个机器人下去遍历完后再回来

————————————————————————————————————————————————

状态转移,使用的“分组背包”思想。

使用一维数组的“分组背包”伪代码如下:

for 所有的组i

    for v=V..0

        for 所有的k属于组i

            f[v]=max{f[v],f[v-c[k]]+w[k]}

——————

可以这么理解:

对于每个根节点root,有个容量为K的背包

如果它有i个儿子,那么就有i组物品,价值分别为dp[son][0],dp[son][1].....dp[son][k] ,这些物品的重量分别为0,1,.....k

现在要求从每组里选一个物品(且必须选一个物品)装进root的背包,使得容量不超过k的情况下价值最大。

那么这就是个分组背包的问题了。

但是这里有一个问题,就是每组必须选一个物品。

对于这个的处理,我们先将dp[son][0]放进背包,如果该组里有更好的选择,那么就会换掉这个物品,否则的话这个物品就是最好的选择。这样保证每组必定选了一个。



#include <iostream>#include <cstring>#include <cstdio>#define INF 0x3f3f3f3f#define BUG printf("here!\n")using namespace std;struct node{    int u,v,w;};node edge[30000];int first[15000],next[30000],cc;int dp[30000][12];int N,S,K;inline void add_edge(int u,int v,int w){    edge[cc].u=u;    edge[cc].v=v;    edge[cc].w=w;    next[cc]=first[u];    first[u]=cc;    cc++;    edge[cc].v=u;    edge[cc].u=v;    edge[cc].w=w;    next[cc]=first[v];    first[v]=cc;    cc++;}void dfs(int u,int p){    int i;    for(i=first[u];i!=-1;i=next[i])    {        int v=edge[i].v;        if(v==p)            continue;        dfs(v,u);        int j,k;        for(j=K;j>=0;j--)        {            dp[u][j]+=dp[v][0]+2*edge[i].w;            for(k=1;k<=j;k++)            {                dp[u][j]=min(dp[u][j],dp[u][j-k]+dp[v][k]+k*edge[i].w);            }        }    }}int Scan(){int res = 0, ch, flag = 0;if((ch = getchar()) == '-')//判断正负flag = 1;else if(ch >= '0' && ch <= '9')//得到完整的数res = ch - '0';while((ch = getchar()) >= '0' && ch <= '9' )res = res * 10 + ch - '0';return flag ? -res : res;}int main(){    while(scanf("%d%d%d",&N,&S,&K)!=EOF)    {        int i;        memset(first,-1,sizeof(first));        memset(next,-1,sizeof(next));        cc=0;        memset(dp,0,sizeof(dp));        for(i=0;i<N-1;i++)        {            int u,v,w;            u=Scan();            v=Scan();            w=Scan();            add_edge(u,v,w);        }        dfs(S,-1);        printf("%d\n",dp[S][K]);    }    return 0;}