zoukankan      html  css  js  c++  java
  • IOI 2005 River (洛谷 3354)

    题目描述

    几乎整个Byteland王国都被森林和河流所覆盖。小点的河汇聚到一起,形成了稍大点的河。就这样,所有的河水都汇聚并流进了一条大河,最后这条大河流进了大海。这条大河的入海口处有一个村庄——名叫Bytetown。

    在Byteland国,有n个伐木的村庄,这些村庄都座落在河边。目前在Bytetown,有一个巨大的伐木场,它处理着全国砍下的所有木料。木料被砍下后,顺着河流而被运到Bytetown的伐木场。Byteland的国王决定,为了减少运输木料的费用,再额外地建造k个伐木场。这k个伐木场将被建在其他村庄里。这些伐木场建造后,木料就不用都被送到Bytetown了,它们可以在运输过程中第一个碰到的新伐木场被处理。显然,如果伐木场座落的那个村子就不用再付运送木料的费用了。它们可以直接被本村的伐木场处理。

    注:所有的河流都不会分叉,形成一棵树,根结点是Bytetown。

    国王的大臣计算出了每个村子每年要产多少木料,你的任务是决定在哪些村子建设伐木场能获得最小的运费。其中运费的计算方法为:每一吨木料每千米1分钱。

    输入输出格式

    输入格式:
    第一行包括两个数 n(2≤n≤100),k(1≤k≤50,且 k≤n)。n为村庄数,k为要建的伐木场的数目。除了Bytetown外,每个村子依次被命名为1,2,3……n,Bytetown被命名为0。

    接下来n行,每行3个整数:

    wi——每年i村子产的木料的块数(0≤wi≤10000)

    vi——离i村子下游最近的村子(即i村子的父结点)(0≤vi≤n)

    di——vi到i的距离(千米)。(1≤di≤10000)

    保证每年所有的木料流到bytetown的运费不超过2,000,000,000分

    50%的数据中n不超过20。

    输出格式:
    输出最小花费,单位为分。

    输入输出样例

    输入样例#1: 复制
    4 2
    1 0 1
    1 1 10
    10 2 5
    1 2 3
    输出样例#1: 复制
    4

    这道题因为村庄呈树形结构,所以选用树形DP。
    自底向上更新答案,dp[x][j][k][0/1] 代表在第x节点处,离x最近的祖先伐木场为j,用了k个伐木场,x节点有/无伐木场的最小花费。先dfs遍历树,开一个stack数组记录每个节点到根节点的距离。之后开始枚举j与k与子树伐木场个数,更新答案。

    #include<iostream>
    #include<cstdio>
    
    using namespace std;
    typedef long long LL;
    const int MAXN = 105;
    
    struct Edge{
        int nxt,to,val;
    }edge[MAXN*2];
                    //size为stack中元素个数,stack记录子节点,d记录深度。
    int n,K,w[MAXN],size,stack[MAXN],d[MAXN];
    int head[MAXN],cnt;
    LL dp[MAXN][MAXN][MAXN][2];
    
    inline void add(int bg,int ed,int ww){
        edge[++cnt].to=ed;
        edge[cnt].val=ww;
        edge[cnt].nxt=head[bg];
        head[bg]=cnt;
    } 
    
    inline void dfs(int u){
        stack[++size]=u;
        for(register int i=head[u];i;i=edge[i].nxt){
            int v=edge[i].to;  //枚举子节点。
            d[v]=d[u]+edge[i].val;  //更新子节点距离。
            dfs(v);
            for(int j=1;j<=size;j++)   //枚举祖先。
                for(int k=K;k>=0;k--){  //枚举u及子树的伐木场个数。
                    dp[u][stack[j]][k][0]+=dp[v][stack[j]][0][0];     
                    dp[u][stack[j]][k][1]+=dp[v][u][0][0];
                    //子节点不建。
                    for(int x=0;x<=k;x++){  //枚举子节点建的伐木场数。
                        dp[u][stack[j]][k][0]=min(dp[u][stack[j]][k-x][0]
                        +dp[v][stack[j]][x][0],dp[u][stack[j]][k][0]);
                        dp[u][stack[j]][k][1]=min(dp[u][stack[j]][k][1]
                        ,dp[u][stack[j]][k-x][1]+dp[v][u][x][0]);
                    }
                }
            }
        for(register int j=1;j<=size;j++)
            for(register int k=0;k<=K;k++){
                if(k>=1){
                    dp[u][stack[j]][k][0]=min(dp[u][stack[j]][k][0]
                    +w[u]*(d[u]-d[stack[j]]),dp[u][stack[j]][k-1][1]);
                }
                else
                    dp[u][stack[j]][k][0]+=w[u]*(d[u]-d[stack[j]]);
            }
            //将dp[][][][0]与dp[][][][1]合并。
        size--;
    }
    int main(){
        scanf("%d%d",&n,&K);
        for(register int i=1;i<=n;i++){
            int fa,ww;
            scanf("%d%d%d",&w[i],&fa,&ww); 
            add(fa,i,ww);
        }
        dfs(0);
        printf("%lld",dp[0][0][K][0]); 
        return 0; 
    }
  • 相关阅读:
    ul中的li设置等宽高css
    Element组件中组件的使用问题
    换个角度聊效率
    【Leetcode 做题学算法周刊】第七期
    【Leetcode 做题学算法周刊】第六期
    【Leetcode 做题学算法周刊】第五期
    【Leetcode 做题学算法周刊】第四期
    【Leetcode 做题学算法周刊】第三期
    【Leetcode 做题学算法周刊】第二期
    【Leetcode 做题学算法周刊】第一期
  • 原文地址:https://www.cnblogs.com/sdfzsyq/p/9677126.html
Copyright © 2011-2022 走看看