首页 > 代码库 > 算法提高 金属采集_树形dp

算法提高 金属采集_树形dp

算法提高 金属采集  
时间限制:1.0s   内存限制:256.0MB
      
问题描述

人类在火星上发现了一种新的金属!这些金属分布在一些奇怪的地方,不妨叫它节点好了。一些节点之间有道路相连,所有的节点和道路形成了一棵树。一共有 n 个节点,这些节点被编号为 1~n 。人类将 k 个机器人送上了火星,目的是采集这些金属。这些机器人都被送到了一个指定的着落点, S 号节点。每个机器人在着落之后,必须沿着道路行走。当机器人到达一个节点时,它会采集这个节点蕴藏的所有金属矿。当机器人完成自己的任务之后,可以从任意一个节点返回地球。当然,回到地球的机器人就无法再到火星去了。我们已经提前测量出了每条道路的信息,包括它的两个端点 x 和 y,以及通过这条道路需要花费的能量 w 。我们想花费尽量少的能量采集所有节点的金属,这个任务就交给你了。

输入格式

第一行包含三个整数 n, S 和 k ,分别代表节点个数、着落点编号,和机器人个数。

接下来一共 n-1 行,每行描述一条道路。一行含有三个整数 x, y 和 w ,代表在 x 号节点和 y 号节点之间有一条道路,通过需要花费 w 个单位的能量。所有道路都可以双向通行。

输出格式
输出一个整数,代表采集所有节点的金属所需要的最少能量。
样例输入
6 1 3
1 2 1
2 3 1
2 4 1000
2 5 1000
1 6 1000
样例输出
3004
样例说明

所有机器人在 1 号节点着陆。

第一个机器人的行走路径为 1->6 ,在 6 号节点返回地球,花费能量为1000。

第二个机器人的行走路径为 1->2->3->2->4 ,在 4 号节点返回地球,花费能量为1003。

第一个机器人的行走路径为 1->2->5 ,在 5 号节点返回地球,花费能量为1001。

数据规模与约定

本题有10个测试点。

对于测试点 1~2 , n <= 10 , k <= 5 。

对于测试点 3 , n <= 100000 , k = 1 。

对于测试点 4 , n <= 1000 , k = 2 。

对于测试点 5~6 , n <= 1000 , k <= 10 。

对于测试点 7~10 , n <= 100000 , k <= 10 。

道路的能量 w 均为不超过 1000 的正整数。

解题思路: 

如果没做过树形dp的可以先做poj2342,大体思路跟那个很像,不过一个点可以放多个机器人。

这样每个点就可以有0,1,2,3、、、个机器人了,就有这么多状态。

每个节点储存以这个节点为根的整个树的花费的最少能量。

然后由整棵树的根节点开始,他跟它的子节点有关系,已知需经历过所有的节点。

可以留在子节点0个--k个机器人,当0个的时候也一定要派一个机器人去,所以:

(dp[m][k]表示以m节点为根的树,来k个机器人花费的最少能量)

 dp[root][j]+=dp[tree[root].son[i].to][0]+2*tree[root].son[i].spend;//等于子节点存0个机器人+一个机器人从父节点到子节点,再从子节点到父节点(即2*cost)。
dp[root][j]=min(   dp[root][j],     dp[root][j-l] + dp[tree[root].son[i].to][l]+l*tree[root].son[i].spend      );//但子节点也可能停留0到j个机器人,所以需要循环一遍再来更新dp[root][j]的值。  我这里用min()函数报错呢,就改成if了。。。
第二个dp里面有j-l,l是从0开始的,如果j从0开始,j-l就成负的了,这里要注意j那个循环要从k开始。
 
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <map>
#include <cmath>
#include <stack>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#define FOR(i,x,n) for(long i=x;i<n;i++)
#define ll long long int
#define INF 0x3f3f3f3f
#define MOD 1000000007
#define MAX_N 60
#define MAX_M 1005

using namespace std;

struct node2{int to;int spend;};
//struct node2 edge;

struct node{
    int number;
    int rating;
    vector<node2> son;//编号,花费
    int father;
};
node tree[100005];
int visable[100005];
int dp[100005][12];//0表示不去,1表示去
int n,S,k;

void dfs(int root){
    visable[root]=1;
    FOR(i,0,tree[root].son.size()){
        if(!visable[tree[root].son[i].to]){
            dfs(tree[root].son[i].to);
            for(int j=k;j>=0;j--){//注意0的时候也要循环
                dp[root][j]+=dp[tree[root].son[i].to][0]+2*tree[root].son[i].spend;
                FOR(l,1,j+1){
                    if(dp[root][j-l]+dp[tree[root].son[i].to][l]+l*tree[root].son[i].spend<dp[root][j]){
                        dp[root][j]=dp[root][j-l]+dp[tree[root].son[i].to][l]+l*tree[root].son[i].spend;
                    }
                }
            }
        }


    }
}

int main()
{
    //freopen("input1.txt", "r", stdin);
    //freopen("data.out", "w", stdout);
    int t1,t2,t3;
    scanf("%d %d %d",&n,&S,&k);
    memset(visable,0,sizeof(visable));
    memset(dp,0,sizeof(dp));
    FOR(i,1,n){
        scanf("%d %d %d",&t1,&t2,&t3);
        node2 t={t2,t3};
        node2 tt={t1,t3};
        tree[t1].son.push_back(t);
        tree[t2].son.push_back(tt);
    }
    dfs(S);
    printf("%d",dp[S][k]);
    //fclose(stdin);
    //fclose(stdout);
    return 0;
}

 

算法提高 金属采集_树形dp