hdu 4003 Find Metal Mineral

描述:

给定一个有n个节点带边权的树,在S点放置K个机器人,问让机器人遍历所有边的最小花费。

分析:

这题简直把我智商给藐视了,一开始我思路完全错了QAQ。。。被卡了半下午

这题难点在于处理机器人进入子树然后回溯的情况,实际上回溯的过程是有一些性质可循的。如果最终状态的子树中有机器人,那么是没有必要再让其他的机器人进入子树再离开子树。我们完全可以让子树中的机器人走一遍准备进入子树再离开的机器人的路径(进入再回到i点),然后从i点继续走自己原本的路径。这样可以省去进出子树的那条边的花费。只有当子树中最终没有机器人,我们才需要回溯。这种情况下会把子树中的每条边都走两遍,只需要一个机器人去回溯就可以了,这样可以最小化进入子树的花费。

我们令dp[i][j]表示以i为根节点的子树中,最终放j个机器人所对应的最小花费。当j=0,则:dp[i][j]=2*子树内总边权。我们把机器人的数目看做容量,花费看做代价,每个子树要么是dp[i][0]+2*w(w为进入子树的那条边的边权,2表示一进一出,容量为0),要么是dp[i][j]+j*w(j个机器人,只进不出,容量为j)。这就是一个分组(每个子树就是一个组)的01背包问题。

代码:

#include<cstdio>
#include<cstring>
using namespace std;
#define N 10005
#define min(x,y) (x<y?x:y)
struct Edge{
    int to,w,next;
};
Edge edge[N<<1];
int dp[N][15],head[N],cnt,S,K,n;
void addedge(int u,int v,int w){
    edge[cnt].to=v;edge[cnt].w=w;
    edge[cnt].next=head[u];head[u]=cnt++;
}
void dfs(int u,int fa){
    int v;
    for(int i=0;i<=K;++i) dp[u][i]=0;
    for(int i=head[u];i!=-1;i=edge[i].next){
        if((v=edge[i].to)==fa) continue;dfs(v,u);
        for(int j=K;j>0;--j){
            dp[u][j]+=(dp[v][0]+2*edge[i].w);
            for(int l=1;l<=j;++l)
                dp[u][j]=min(dp[u][j],dp[u][j-l]+dp[v][l]+l*edge[i].w);
        }
        dp[u][0]+=(dp[v][0]+2*edge[i].w);
    }
}
int main(){
    int u,v,w;
    while(scanf("%d%d%d",&n,&S,&K)!=EOF){
        cnt=0;memset(head,-1,sizeof(head));
        for(int i=1;i<n;++i){
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);addedge(v,u,w);
        }
        dfs(S,0);
        printf("%d\n",dp[S][K]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值