zoukankan      html  css  js  c++  java
  • BZOJ_1812_[Ioi2005]riv_树形DP

    BZOJ_1812_[Ioi2005]riv_树形DP

    Description

    几乎整个Byteland王国都被森林和河流所覆盖。小点的河汇聚到一起,形成了稍大点的河。就这样,所有的河水都汇聚并流进了一条大河,最后这条大河流进了大海。这条大河的入海口处有一个村庄——名叫Bytetown 在Byteland国,有n个伐木的村庄,这些村庄都座落在河边。目前在Bytetown,有一个巨大的伐木场,它处理着全国砍下的所有木料。木料被砍下后,顺着河流而被运到Bytetown的伐木场。Byteland的国王决定,为了减少运输木料的费用,再额外地建造k个伐木场。这k个伐木场将被建在其他村庄里。这些伐木场建造后,木料就不用都被送到Bytetown了,它们可以在 运输过程中第一个碰到的新伐木场被处理。显然,如果伐木场座落的那个村子就不用再付运送木料的费用了。它们可以直接被本村的伐木场处理。 注意:所有的河流都不会分叉,也就是说,每一个村子,顺流而下都只有一条路——到bytetown。 国王的大臣计算出了每个村子每年要产多少木料,你的任务是决定在哪些村子建设伐木场能获得最小的运费。其中运费的计算方法为:每一块木料每千米1分钱。 编一个程序: 1.从文件读入村子的个数,另外要建设的伐木场的数目,每年每个村子产的木料的块数以及河流的描述。 2.计算最小的运费并输出。

    Input

    第一行 包括两个数 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村子下游最近的村子(或bytetown)(0<=vi<=n) di——vi到i的距离(km)。(1<=di<=10000) 保证每年所有的木料流到bytetown的运费不超过2000,000,000分 50%的数据中n不超过20。

    Output

    输出最小花费,精确到分。

    Sample Input

    4 2
    1 0 1
    1 1 10
    10 2 5
    1 2 3

    Sample Output

    4

    这题绝对好题,看了https://www.cnblogs.com/CQzhangyu/p/7605481.html 后才明白怎么转移的。
    设F[i][j][k]表示以i为根的子树中,建了k个伐木场,i的祖先最近的一个建伐木场的位置到i的距离为j。
    这样我们把代价提前计算。
    转移类似背包,按子树合并,dfs中更新祖先。
    F[i][j][k+l]=F[i][j][k]+min(F[son][j][l],F[son][0][l]),分别表示儿子不放/放伐木场。
     
    代码:
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define N 205
    int head[N],to[N],nxt[N],val[N],w[N],cnt,fa[N][N],siz[N],sf[N],dis[N],g[N][N],m,n;
    int f[N][N][N];
    inline void add(int u,int v,int z) {
        to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; val[cnt]=z;
    }
    void dfs(int x) {
        int i,j,k,l;siz[x]=1;
        fa[x][0]=x;
        for(i=0;i<=sf[x];i++) {
            f[x][i][!i]=(dis[x]-dis[fa[x][i]])*w[x];
        }
        for(i=head[x];i;i=nxt[i]) {
            dis[to[i]]=dis[x]+val[i];
            for(j=0;j<=sf[x];j++) fa[to[i]][++sf[to[i]]]=fa[x][j];
            dfs(to[i]);
            memset(g,0x3f,sizeof(g));
            for(j=0;j<=sf[x];j++) {
                for(k=min(m,siz[x]);k>=0;k--) {
                    for(l=min(m-k,siz[to[i]]);l>=0;l--) {
                        g[j][k+l]=min(g[j][k+l],f[x][j][k]+min(f[to[i]][j+1][l],f[to[i]][0][l]));
                    }
                }
            }
            siz[x]+=siz[to[i]];
            for(j=0;j<=sf[x];j++) {
                for(k=min(m,siz[x]);k>=0;k--) {
                    f[x][j][k]=g[j][k];
                }
            }
        }
    }
    int main() {
        memset(f,0x3f,sizeof(f));
        // freopen("riv.in","r",stdin);
        // freopen("riv.out","w",stdout);
        scanf("%d%d",&n,&m);m++;
        int i,z;
        for(i=1;i<=n;i++) {
            scanf("%d%d%d",&w[i],&fa[i][1],&z);
            add(fa[i][1],i,z);
        }
        dfs(0);
        printf("%d
    ",f[0][0][m]);
    }
    /*
    4 2
    1 0 1
    1 1 10
    10 2 5
    1 2 3
    */
     
    /*
    8 2
    233 0 5
    9 1 80
    27 2 20
    64 1 100
    2 3 14
    81 4 5
    10 3 70
    10 5 8
    */
    
  • 相关阅读:
    matlab函数集锦
    视觉(1)[导入]EStereo
    我为啥申请这里的博客?
    [导入]给定一个英文原文,统计文件里面一共有多少个不同的英文单词
    视觉(2)[导入]opengl贴图程序
    C++ Builder 控件的卸载<转载>
    [转]<不知道能否解决先转下来再说>不显示删除回复显示所有回复显示星级回复显示得分回复 没有找到MSVR90D.dll因此这个应用程序未能启动
    终于把《windows程序设计》上册读完了,hoho
    乱思。。。。。。。、、、、、
    测试windows live writer
  • 原文地址:https://www.cnblogs.com/suika/p/9204290.html
Copyright © 2011-2022 走看看