HDU 4003Find Metal Mineral

题意: 给你一颗N个节点的树,和K个机器人从S点出发,问走遍所有点的最小路程是?机器人可以走回头路。

思路:这题情形是:如果树某一个节点的分支数>k,那么必然会有从根节点派往子节点并且折回到根节点,就是会走重复的边,就像第一个案例。

设 dp[i][j] : 表示在i节点有派了j个机器人的最小路程耗费,定义DP[i][0]存放1个机器人,从子节点返回到根节点的花费。

思考,(1)如果只有一个机器人,那么只有一种方法,就是用这一个机器人跑遍每个子节点V、也就是每次机器人都要从一个子节点返回根节点,然后走向另一个节点,直到遍历所有点位止。那么这种状态下的状态转移为:dp[u][j]+=dp[v][0]+w*2;(u是当前结点,v是u的子节点,w是u,v之间边的权值)。

(2)当机器人的个数>1时,当前的决策就是派多少机器人去遍历剩下的子节点。思考发现这就是一个很典型的分组背包问题。

以下这段摘自别人的分析:

可以这么理解:

对于每个根节点root,有个容量为K的背包

如果它有i个儿子,那么就有i组物品,价值分别为dp[son][0],dp[son][1].....dp[son][k] ,这些物品的重量分别为0,1,.....k

现在要求从每组里选一个物品(且必须选一个物品)装进root的背包,使得容量不超过k的情况下价值最大。

那么这就是个分组背包的问题了。

但是这里有一个问题,就是每组必须选一个物品。

对于这个的处理,我们先将dp[son][0]放进背包,如果该组里有更好的选择,那么就会换掉这个物品,否则的话这个物品就是最好的选择。这样保证每组必定选了一个。


那么就可以得到

dp[root][j]=min(dp[root][j-k]+dp[son][k]+k*w);的转移方程,相当于选择当前背包大小为k的背包。

#include<bits/stdc++.h>
using namespace std;
template<int N,int M>//N点的个数,M边的个数
struct Graph{
    int top;
    struct Vertex{
        int head;
    }V[N];
    struct Edge{
        int v,next,w;
    }E[M];
    void init(){
        memset(V,-1,sizeof(V));
        top = 0;
    }
    void add_edge(int u,int v,int w){
        E[top].v = v;
        E[top].next = V[u].head;
        E[top].w=w;
        V[u].head = top++;
    }
};
const int maxn=10000+10;
Graph<maxn,maxn*2> g;
int N,S,K,dp[maxn][15];

void dfs(int u,int f){
   for(int i=g.V[u].head;i!=-1;i=g.E[i].next){
      int v=g.E[i].v,w=g.E[i].w;
      if(v==f) continue;
      dfs(v,u);
      for(int j=K;j>=0;j--){
        dp[u][j]+=dp[v][0]+2*w;
        for(int k=1;k<=j;k++){
            dp[u][j]=min(dp[u][j],dp[u][j-k]+dp[v][k]+k*w);
        }
      }
   }
}

int main(){
    while(~scanf("%d%d%d",&N,&S,&K)){
       g.init();
       for(int i=0;i<N-1;i++){
           int a,b,c;scanf("%d%d%d",&a,&b,&c);
           g.add_edge(a,b,c);
           g.add_edge(b,a,c);
       }
       memset(dp,0,sizeof(dp));
       dfs(S,-1);
       printf("%d\n",dp[S][K]);
    }
}
题外话:改编一下,如果规定出发点不限,应该怎么做呢?(我还没想到..)如果规定机器人只有一个,并且出发点不限,那就变成hdu4607。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值