HDU-4003 Find Metal Mineral 树形dp

题意

输入描述
There are multiple cases in the input.
In each case:
The first line specifies three integers N, S, K specifying the numbers of metal mineral, landing site and the number of robots.
The next n‐1 lines will give three integers x, y, w in each line specifying there is a path connected point x and y which should cost w.
1<=N<=10000, 1<=S<=N, 1<=k<=10, 1<=x, y<=N, 1<=w<=10000.
输出描述
For each cases output one line with the minimal energy cost.
题目大意
火星上有一种金属材料,需要去获得,火星的地图可以看做一棵有N个节点的树,其根节点为S,你要派去K个机器人从S出发去遍历所有的节点以获得材料。再告诉你相互连接的点与相应的花费,若机器人可以从任意节点将所收集到的材料带回地球,询问获取到所有材料的最少花费。

题解

思路
首先要明确的是,既然机器人可以从任意节点返回,那么我们就用f[x][i]来表示在以x节点为根节点的子树上花费 i个机器人需要的最少花费。什么叫花费呢,这i个机器人最终将停留在x子树上,也就是说最后这些机器人是从这棵子树上的某个节点返回的。比如f[x][0]表示说没有机器人会待在这棵子树上,它们进去遍历完子节点后都会出来。
那么剩下的问题就好解决了。由于必须要遍历所有节点,那么每个子树都要有机器人去,那么对于子树的状态,选且仅能选一种。那不就是一个树上分组背包嘛,我们可以将子树视作一组,物品即为子树的状态。
代码

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int size=10100;
struct date{
    int v,w,nxt;
}edge[size<<1];
int n,s,k,p,ans,head[size],f[size][15];
int min(int x,int y){return x<y?x:y;}
void add(int u,int v,int w)//链式前向星,很好用的储存方法
{
    edge[++p].v=v;
    edge[p].w=w;
    edge[p].nxt=head[u];
    head[u]=p;
}
bool input()
{
    int u,v,w;
    if(scanf("%d",&n)==EOF)
      return false;
    scanf("%d%d",&s,&k);
    p=0;
    memset(head,0,sizeof(head));
    memset(f,0,sizeof(f));
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
        add(v,u,w);//要建反向边
    }
    return true;
}
void dp(int x,int pre)
{
    for(int i=head[x];i;i=edge[i].nxt)
    {
        if(edge[i].v==pre)//避免搜到前驱节点
          continue;
        dp(edge[i].v,x);
        for(int j=k;j>=0;j--)
        {
            f[x][j]+=f[edge[i].v][0]+edge[i].w*2;//因为要回来,所以代价*2 
            for(int r=1;r<=j;r++)
              f[x][j]=min(f[x][j],f[x][j-r]+f[edge[i].v][r]+r*edge[i].w);//r个机器人留在x子树中 
        }
    }
}
int main()
{
    while(input())
    {
        dp(s,-1);
        printf("%d\n",f[s][k]);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值