HDU 4003 Find Metal Mineral(树形DP+分组背包)
Find Metal Mineral
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65768/65768 K (Java/Others)
Total Submission(s): 1441 Accepted Submission(s): 631
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.
——求K个机器人从同一点出发,遍历所有点所需的最小花费
——树形DP,分组背包
——url:http://acm.hdu.edu.cn/showproblem.php?pid=4003
————————————————————————————————————————————————
【转载】:
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]放进背包,如果该组里有更好的选择,那么就会换掉这个物品,否则的话这个物品就是最好的选择。这样保证每组必定选了一个。
/* HDU 4003 树形DP+只能选一个物品的分组背包 dp[pos][num]表示以pos为根节点的子树下,用去num个机器人,所得到的最小值 特别的是当num==0的时候,dp[pos][0]表示用一个机器人去走完所有子树,最后又回到pos这个节点 状态转移:dp[pos][num]=min∑{dp[pos_j][num_j]+w_j},pos_j是pos的所有儿子, 为了让分组背包只选一个,首先让dp[u][0]如背包 使用一维数组的“分组背包”伪代码如下: for 所有的组i for v=V..0 for 所有的k属于组i f[v]=max{f[v],f[v-c[k]]+w[k]} */ #include<stdio.h> #include<algorithm> #include<string.h> #include<iostream> using namespace std; const int MAXN=10010; struct Node { int to; int next; int cost;//该条路径的花费 }edge[MAXN*2];//无向树 int head[MAXN]; int tol; int dp[MAXN][11];//dp[i][j]表示以i为根的树用掉j个人。dp[i][0]表示用了一个人又回到上面的点 void init() { memset(head,-1,sizeof(head)); tol=0; memset(dp,0,sizeof(dp)); } void add(int a,int b,int val) { edge[tol].to=b; edge[tol].cost=val; edge[tol].next=head[a]; head[a]=tol++; edge[tol].to=a; edge[tol].cost=val; edge[tol].next=head[b]; head[b]=tol++; } int N,K; void dfs(int u,int pre) { for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].to; if(v==pre)continue; dfs(v,u); for(int k=K;k>=0;k--) { dp[u][k]+=dp[v][0]+2*edge[i].cost;//先把dp[u][0]放进背包,保证至少选一个 // 分组背包 for(int j=1;j<=k;j++) dp[u][k]=min(dp[u][k],dp[u][k-j]+dp[v][j]+j*edge[i].cost); } } } int main() { int S; int a,b,val; while(scanf("%d%d%d",&N,&S,&K)!=EOF) { init(); for(int i=1;i<N;i++) { scanf("%d%d%d",&a,&b,&val); add(a,b,val); } dfs(S,-1); printf("%d\n",dp[S][K]); } return 0; }