zoukankan      html  css  js  c++  java
  • Factories(Gym102222G)(树形dp+背包)

    题意:

    给你一棵由 (n) 个结点构成的树,树的每条边都有权值,问在树的叶子结点上选定 (k) 个结点,使任意两个结点之间的距离之和最短,求出最小值。


    想法:

    • 结点之间距离之和最短问题,我们考虑每条边的贡献。此类问题大多数可以用树形dp解决。
    • (dp[i][j]) 表示编号为 (i) 的结点为根的子树中有 (j) 个叶子结点被使用时的最优解。
    • 考虑转移方程:(dp[u][x]=min(dp[u][x],dp[u][x-y]+dp[v][y]+w imes (k-y) imes y)),其中 (u)为当前结点, (v)(u) 的父亲结点, (w)(dis(u,v))
    • 那么对于每一对父子,我们都可以通过背包去求最优解,每次枚举范围一定是 (1sim min(k,siz[u]))(siz[u]) 表示以 (u) 为根的子树的叶子数。
    • 只要先选定一个非叶子结点作为根 (rt) ,然后一步步向叶子结点转移,最后答案就是 (dp[rt][k])

    代码:

    struct node{
        int nxt;
        ll w;
    };
    int n,k;
    vector<node>g[100005];
    ll dp[100005][105];
    int in[100005];
    int siz[100005];
    void dfs(int u,int fa)
    {
        if(in[u]==1){
            siz[u]=1;
            dp[u][1]=0;
        }
        for(int i=0;i<g[u].size();i++){
            int v=g[u][i].nxt;
            ll w=g[u][i].w;
            if(v==fa)continue;
            dfs(v,u);
            siz[u]+=siz[v];
            for(int x=min(k,siz[u]);x>=1;x--){
                for(int y=1;y<=min(x,siz[v]);y++){
                    dp[u][x]=min(dp[u][x],dp[u][x-y]+dp[v][y]+w*(k-y)*y);
                }
            }
        }
    }
    int main()
    {
        int T,CASE=0;
        cin>>T;
        while(T--){
            scanf("%d%d",&n,&k);
            for(int i=0;i<=n;i++)g[i].clear();
            mem(in,0);
            mem(siz,0);
            for(int i=1;i<n;i++){
                int u,v;
                ll w;
                scanf("%d%d%lld",&u,&v,&w);
                in[u]++;
                in[v]++;
                g[u].push_back((node){v,w});
                g[v].push_back((node){u,w});
            }
            int rt=1;
            for(int i=1;i<=n;i++){
                if(in[i]>1){
                    rt=i;
                    break;
                }
            }
            for(int i=1;i<=n;i++){
                dp[i][0]=0;
                for(int j=1;j<=k;j++){
                    dp[i][j]=INF;
                }
            }
            dfs(rt,-1);
            printf("Case #%d: ",++CASE);
            printf("%lld
    ",dp[rt][k]);
        }
    }
    
    越自律,越自由
  • 相关阅读:
    tyvj P3737 逐个击破
    最小生成树 kruskal
    最小生成树 prim
    code2039 骑马修栏杆
    前端优化
    数组1
    数组
    序列化对象
    解决iphone横屏时字体变大问题或者内容大小不一样等...
    Media Queries详解
  • 原文地址:https://www.cnblogs.com/ha-chuochuo/p/14391043.html
Copyright © 2011-2022 走看看