题目链接: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;
}
}