HDU-4003 Find Metal Mineral 树形dp

来源:互联网 发布:怎么样注册淘宝店 编辑:程序博客网 时间:2024/04/26 01:17

题意

输入描述
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;}
原创粉丝点击