题意:求k个机器人从同一点出发遍历整棵树所要的最小花费。树中边有权值。
分析:
1、这是带回溯的树形dp,和apple tree一样。这题是边权,apple tree是点权。
2、很明显是至少有一个机器人遍历整棵树。分给子树遍历的机器人越多,从子树的根回到它的父节点走
的重复路程也越多。“至少有一个”联想到分组背包。
3、dp[i][0]表示1个机器人遍历整棵树,不分给以i为根节点的树的子树另外的机器人。
把总的机器人个数当做背包容积,边权当作价值。
for(int j=V;j>=0;j--)
for(int k=1;k<=j;k++)
dp[s][j]=min(dp[s][j],dp[s][j-k]+dp[v][k]+edge(s,v)*k);
分k(1<=k<=j)个机器人遍历v为根的子树。
为了保证至少选一个,先把dp[v][0]放入背包。
#include<cstdio> #include<iostream> #include<cstring> using namespace std; int dp[10010][12],head[10010]; int V,tot; struct node { int to,val; int next; }edge[10010*2]; int minn(int a,int b) { return a<b?a:b; } void add(int x,int y,int c) { edge[tot].to=y; edge[tot].val=c; edge[tot].next=head[x]; head[x]=tot++; } void init() { memset(dp,0,sizeof(dp)); memset(head,-1,sizeof(head)); tot=0; } void dfs(int u,int pre) { int i; for(i=head[u];i!=-1;i=edge[i].next) { int t=edge[i].to; if(t==pre) //如果t是父亲节点continue; continue; dfs(t,u); for(int j=V;j>=0;j--) { dp[u][j]+=dp[t][0]+2*edge[i].val; //保证至少选一个 for(int k=1;k<=j;k++) dp[u][j]=minn(dp[u][j],dp[u][j-k]+dp[t][k]+k*edge[i].val); } } } int main() { int n,s,a,b,cost; while(scanf("%d%d%d",&n,&s,&V)!=EOF) { init(); for(int i=1;i<n;i++) { scanf("%d%d%d",&a,&b,&cost); add(a,b,cost); add(b,a,cost); } dfs(s,-1); printf("%d\n",dp[s][V]); } return 0; }