http://www.cnblogs.com/kuangbin/archive/2012/08/29/2661928.html
【转载】:
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]放进背包,如果该组里有更好的选择,那么就会换掉这个物品,否则的话这个物品就是最好的选择。这样保证每组必定选了一个。
#include <iostream> #include <cstring> #include <cstdio> #define INF 0x3f3f3f3f #define BUG printf("here!\n") using namespace std; struct node { int u,v,w; }; node edge[30000]; int first[15000],next[30000],cc; int dp[30000][12]; int N,S,K; inline void add_edge(int u,int v,int w) { edge[cc].u=u; edge[cc].v=v; edge[cc].w=w; next[cc]=first[u]; first[u]=cc; cc++; edge[cc].v=u; edge[cc].u=v; edge[cc].w=w; next[cc]=first[v]; first[v]=cc; cc++; } void dfs(int u,int p) { int i; for(i=first[u];i!=-1;i=next[i]) { int v=edge[i].v; if(v==p) continue; dfs(v,u); int j,k; for(j=K;j>=0;j--) { dp[u][j]+=dp[v][0]+2*edge[i].w; for(k=1;k<=j;k++) { dp[u][j]=min(dp[u][j],dp[u][j-k]+dp[v][k]+k*edge[i].w); } } } } int Scan() { int res = 0, ch, flag = 0; if((ch = getchar()) == '-') //判断正负 flag = 1; else if(ch >= '0' && ch <= '9') //得到完整的数 res = ch - '0'; while((ch = getchar()) >= '0' && ch <= '9' ) res = res * 10 + ch - '0'; return flag ? -res : res; } int main() { while(scanf("%d%d%d",&N,&S,&K)!=EOF) { int i; memset(first,-1,sizeof(first)); memset(next,-1,sizeof(next)); cc=0; memset(dp,0,sizeof(dp)); for(i=0;i<N-1;i++) { int u,v,w; u=Scan(); v=Scan(); w=Scan(); add_edge(u,v,w); } dfs(S,-1); printf("%d\n",dp[S][K]); } return 0; }