HDU-4003 Find Metal Mineral (树形DP+分组背包)
题目大意:用m个机器人去遍历有n个节点的有根树,边权代表一个机器人通过这条边的代价,求最小代价。
题目分析:定义状态dp(root,k)表示最终遍历完成后以root为根节点的子树中有k个机器人时产生的总代价。则状态转移方程为:
dp(root,k)=min(dp(root,k),dp(son,j)+dp(root,k-j)+j*w(root,son)) j>0
要注意,当j为0的时候表示遍历完son这个子树后所有的机器人都回到root。可以证明,如果让遍历son的所有的机器人都回到root,那么遍历son的机器人越多,产生的代价就越高。
代码如下:
# include<iostream> # include<cstdio> # include<cstring> # include<algorithm> using namespace std; # define LL long long const int N=10005; struct Edge { int to,w,nxt; }; Edge e[N<<1]; int head[N]; int cnt,n,s,m; int dp[N][12]; void add(int u,int v,int w) { e[cnt].to=v; e[cnt].w=w; e[cnt].nxt=head[u]; head[u]=cnt++; } void init() { int a,b,w; cnt=0; memset(dp,0,sizeof(dp)); memset(head,-1,sizeof(head)); for(int i=1;i<n;++i){ scanf("%d%d%d",&a,&b,&w); add(a,b,w); add(b,a,w); } } void dfs(int u,int fa) { for(int i=head[u];i!=-1;i=e[i].nxt){ int v=e[i].to; if(v==fa) continue; dfs(v,u); for(int j=m;j>=0;--j){ dp[u][j]+=dp[v][0]+2*e[i].w; for(int k=1;k<=j;++k) dp[u][j]=min(dp[u][j],dp[u][j-k]+dp[v][k]+k*e[i].w); } } } void solve() { dfs(s,-1); printf("%d\n",dp[s][m]); } int main() { while(~scanf("%d%d%d",&n,&s,&m)) { init(); solve(); } return 0; }