hdu 4003 Find Metal Mineral(树形dp+分组背包)

来源:互联网 发布:使用c语言完成http请求 编辑:程序博客网 时间:2024/03/29 12:43

Find Metal Mineral

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=4003

解题思路:

题目大意:

给你一棵n个节点的树, 节点编号为1~n, 每条边都有一个花费值。有k个机器人从S点出发, 问让机器人遍历所有边,最少花费值多少?

算法思想:

根据题意可以知道,机器人是可以走回头路的。所以分析下可知,如果从根节点派往子节点如果要折回到根节点,派向这个子节点

的机器人越多,产生的重复路径就会越多,所以那么派往这个子节点的个数为1时是最优策略。那么定义dp[i][0]存放1个机器人,从

子节点返回到根节点的花费。


现在思考:如果根节点s, 有 n个子节点,那么要怎么样选择才能使dp[s][k]的值最小呢?

⑴当机器人的个数为1时,那么只有一种方法,就是用这一个机器人跑遍每个子节点、也就是每次机器人都要从一个子节点返回根节

点,然后走向另一个节点,直到遍历所有点为止。那么这种状态下的状态转移为: dp[s][0]=dp[next][0]+2*w;       (其中s为根节点,

next为子节点,w为s到next的权值)。

⑵当机器人的个数>1时,那么必然存在机器人不必返回根节点的策略。同时由⑴也可以求出折回根节点的花费。

那么求根节点s下的n个子节点,把每个节点看做一个整体,定义dp(n,j)为前n个节点派j个机器人的最小花费,其实就是个包,dp(n,j)

dp(n-1,j)必然存在某种联系。可以推出dp(n,j)=min(dp(n,j), dp(n-1,j-k)+dp[v][k]+k*w);  (1<=k<=j,w为s到当前这个子节点v的权值),

因为只要用到上一个子节点的状态。所以对起始点s的所有子节点扫描一遍就可以了。

AC代码:

#include <bits/stdc++.h>using namespace std;const int maxn = 10005;struct node{    int v,w;    node(int _v,int _w):v(_v),w(_w){}};vector<node> path[maxn];int dp[maxn][15];//节点i,向子节点放j个机器人的最小花费int n,s,k;void dfs(int u,int father){    int len = path[u].size();    for(int i = 0; i < len; i++){        int next = path[u][i].v;        int w = path[u][i].w;        if(father == next)            continue;                     //如果当前的节点为父亲节点,不往下走。        if(father == -1 || father != next){//如果u为s点 (最上面的那个根节点)或者next不等于父亲节点            //cout<<next<<" "<<u<<endl,            dfs(next,u);        }        //cout<<next<<endl;        for(int j = k; j >= 1; j--){            dp[u][j] += dp[next][0] + 2*w;            for(int jj = 1; jj <= j; jj++)                dp[u][j] = min(dp[u][j],dp[u][j-jj]+dp[next][jj]+jj*w);        }        dp[u][0] += dp[next][0] + 2*w;    }}int main(){    while(~scanf("%d%d%d",&n,&s,&k)){        for(int i = 0; i <= 10000; i++)            path[i].clear();        memset(dp,0,sizeof(dp));        int u,v,w;        for(int i = 0; i < n-1; i++){            scanf("%d%d%d",&u,&v,&w);            path[u].push_back(node(v,w));            path[v].push_back(node(u,w));        }        dfs(s,-1);        printf("%d\n",dp[s][k]);    }    return 0;}


0 0
原创粉丝点击