题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4003
题意:有n个矿,矿之间有n-1条路,每条路有相应花费。有k个机器人,从s点出发,求遍历每个矿的最小花费。
对于一个根节点s,如果机器人足够遍历所有的子树,可以看做常规的树状dp,分配c个机器人去遍历子树,求最优解。
如果机器人不够时,那么就从别的子树那里借一些机器人,从根节点出法,遍历结束后返回根节点还给别的子树。那借几个机器人是最优状况?由于每个节点都需要遍历,并且机器人要返回根节点,所以每条路都至少需要走两遍。若借的机器人越多,重复走的路也就越多(树形结构),所以只需要一个机器人。综上可知,当机器人数为0的时候,借一个机器人遍历子树,然后再返回根节点。
#include <iostream> #include<cstdio> #include<cstring> #include<cmath> #include<vector> #define N 11000 using namespace std; struct node { int u,w; node(int a,int b):u(a),w(b){} }; vector<node> r[N]; int d[N][12],v[N],n,s,k; void dfs(int t) { v[t]=1; for(int i=0;i<r[t].size();i++) { int u=r[t][i].u; int w=r[t][i].w; if(v[u]) continue; dfs(u); for(int j=k;j>=0;j--) { d[t][j]+=d[u][0]+2*w; //初始化前i棵子树的最小花费。由于前i-1的情况已求,只需要加上第i棵子树的最坏情况。 for(int c=1;c<=j;c++) d[t][j]=min(d[t][j],d[t][j-c]+d[u][c]+w*c);//派遣c个机器人去子树。 } } } int main() { while(~scanf("%d%d%d",&n,&s,&k)) { for(int i=0;i<=n;i++) r[i].clear(); for(int i=1;i<n;i++) { int u,c,w; scanf("%d%d%d",&u,&c,&w); r[u].push_back(node(c,w)); r[c].push_back(node(u,w)); } memset(d,0,sizeof(d)); memset(v,0,sizeof(v)); dfs(s); cout<<d[s][k]<<endl; } }