首页 > 代码库 > 算法提高 金属采集_树形dp
算法提高 金属采集_树形dp
人类在火星上发现了一种新的金属!这些金属分布在一些奇怪的地方,不妨叫它节点好了。一些节点之间有道路相连,所有的节点和道路形成了一棵树。一共有 n 个节点,这些节点被编号为 1~n 。人类将 k 个机器人送上了火星,目的是采集这些金属。这些机器人都被送到了一个指定的着落点, S 号节点。每个机器人在着落之后,必须沿着道路行走。当机器人到达一个节点时,它会采集这个节点蕴藏的所有金属矿。当机器人完成自己的任务之后,可以从任意一个节点返回地球。当然,回到地球的机器人就无法再到火星去了。我们已经提前测量出了每条道路的信息,包括它的两个端点 x 和 y,以及通过这条道路需要花费的能量 w 。我们想花费尽量少的能量采集所有节点的金属,这个任务就交给你了。
第一行包含三个整数 n, S 和 k ,分别代表节点个数、着落点编号,和机器人个数。
接下来一共 n-1 行,每行描述一条道路。一行含有三个整数 x, y 和 w ,代表在 x 号节点和 y 号节点之间有一条道路,通过需要花费 w 个单位的能量。所有道路都可以双向通行。
1 2 1
2 3 1
2 4 1000
2 5 1000
1 6 1000
所有机器人在 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