zoukankan      html  css  js  c++  java
  • HAOI2010 软件安装 有依赖的背包DP

      

    题目描述

    现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi。我们希望从中选择一 些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和最大)。

    但是现在有个问题:软件之间存在依赖关系,即软件i只有在安装了软件j(包括软件j的直接或间接依赖)的情况下才能正确工作(软件i依赖软件j)。幸运的 是,一个软件最多依赖另外一个软件。如果一个软件不能正常工作,那么它能够发挥的作用为0。

    我们现在知道了软件之间的依赖关系:软件i依赖软件Di。现在请你设计出一种方案,安装价值尽量大的软件。一个软件只能被安装一 次,如果一个软件没有依赖则Di=0,这时只要这个软件安装了,它就能正常工作。

    输入格式

    第1行:N,M (0<=N<=100.0<=M<=500)

    第2行:W1,W2,…,Wi,…,Wn(0<=Wi<=M)

    第3行:V1,V2,…,Vi,…,Vn(0<=Vi<=1000)

    第4行:D1,D2,…,Di,…,Dn(0<=Di<=N,Di≠i)

    输出格式

    一个整数,代表最大价值。

    样例

    样例输入

    3 10
    5 5 6
    2 3 4
    0 1 1

    样例输出

    5

      用这个题呢,首先来复习一下背包DP,f[i][v]=max(f[i][v],f[i-1][v-c[i]]+w[i]) 然后发现只从i-1到i转移,直接搞掉第一维,然后调整循环顺序,采用v到0的倒序保证只取一次,然后强一点的背包是完全和多重,完全可以把循环顺序反过来,保证随意取,而多重可以用二进制优化,当然单队也是可以的,这里不再赘述,具体可以参见DD大牛的背包九讲(看了很多遍),那么在简单的分组DP中,保证一个组只取一个或者不去,我们的转移应该这样写;

      for  每一组

        for v...0

          for 每一个元素

            f[i][v]=max(f[i][v],f[i-1][v-c[i]]+w[i];

    至于循环顺序,必须将枚举体积套在枚举每一元素的外面,如果放里面其实就和没有组别一样了,不能保证每组只能取一个。

      有依赖的DP就和分组DP很像,我们可以把没有依赖的物品称主件,有依赖的称为附件,本题是在树上,那么一定要定义给以i为根的子树分配V的空间所能达到的最大价值,那然后发现多个儿子转移到父亲似乎不好转移,父亲好像要给儿子分配时间。

      重新考虑一下,对于一个主件对应的几个附件可以看作是决策集合,那么这个集合在子树中有指数级多的的决策,怎么办呢?如果我们优化的话,对于每一个决策,我们要找相同体积找价值最大的,这是显然的,然后从子树转移过来的时候,我们会发现所谓的f[i][v]就是V体积下对应的那个最大的价值,决策已经保证最优,只要循环V转移即可,道理同上。

      别的扯完了,我们可以看一看这个题目,每个物品只有一个出边,仍然可能是基环森林。。。。那么可以通过分析得到,这题取软件不分先后顺序,只要我的依赖取上就行,哪怕是在我后边取,那么一个环共生共灭,然后就可以缩点了,缩点真麻烦,缩完真简便。。。只要在缩完点的森林中建一个虚根0,价值和体积都为0,然后就成了0为根的一棵树,然后这个题就被我们一步一步肢解了。。

      最后说一句,如何保证父亲不取的时候子树均不能做贡献。

      (只要在第二层循环体积的时候只循环到v[x],别的不更新即可。

      

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<algorithm>
    using namespace std;
    const int N=150,V=550;
    vector<int> q[N];
    int fr[N],ft[N],s[N],w[N],v[N],size[N],stack[N],du[N],c[N],low[N],vi[N],wi[N],dfn[N],rt=-1,num,cnt,kt,top,n,m,tt,tp,f[N][V];
    bool ins[N],b[N];
    struct node{int fr,to,pr;}mo[N],no[N];
    struct qe{int a,b;};
    int rd()
    {
        char cc=getchar();
        int s=0,w=1;
        while(cc<'0'||cc>'9') {if(cc=='-') w=-1;cc=getchar();}
        while(cc>='0'&&cc<='9') s=(s<<3)+(s<<1)+cc-'0',cc=getchar();
        return s*w;
    }
    void init(int x,int y)
    {
        no[++tp].fr=x;
        no[tp].to=y;
        no[tp].pr=ft[x];
        ft[x]=tp;du[y]++;
    }
    void add(int x,int y)
    {
        mo[++tt].fr=x;
        mo[tt].to=y;
        mo[tt].pr=fr[x];
        fr[x]=tt;
    }
    void tarjan(int x)
    {
        low[x]=dfn[x]=++num;
        stack[++top]=x;ins[x]=1;
        for(int i=fr[x];i;i=mo[i].pr)
        {
            int y=mo[i].to;
            if(!dfn[y])
            {
                tarjan(y);
                low[x]=min(low[x],low[y]);
            }
            else if(ins[y]) low[x]=min(low[x],dfn[y]);
        }
        if(low[x]==dfn[x])
        {
            int y,s=0;++cnt;
            do{
                y=stack[top--];
                vi[cnt]+=v[y];
                wi[cnt]+=w[y];
                c[y]=cnt;
                ins[y]=0;
            }while(x!=y);
        }
    }
    void rebuild()
    {
        for(int x=1;x<=n;x++) 
            for(int i=fr[x];i;i=mo[i].pr)
            {
                int to=mo[i].to;
                if(c[x]!=c[to]) init(c[x],c[to]);
            }
        for(int i=1;i<=cnt;i++)
            if(!du[i]) init(0,i);
    }
    void dp(int x)
    {
        for(int i=m;i>=vi[x];i--) f[x][i]=max(f[x][i],f[x][i-vi[x]]+wi[x]);
        for(int i=ft[x];i;i=no[i].pr)
        {
            int to=no[i].to;
            dp(to);
            for(int i=m;i>=vi[x];i--)
                for(int j=0;j<=i-vi[x];j++)
                f[x][i]=max(f[x][i],f[x][i-j]+f[to][j]);
        }
    }
    int main()
    {
        n=rd();m=rd();
        int x,y,mn=-0x7fffffff;
        for(int i=1;i<=n;i++) v[i]=rd();
        for(int i=1;i<=n;i++) w[i]=rd();
        for(int i=1;i<=n;i++)
        {
            x=rd();
            if(x!=0) add(x,i);
        }
        for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
        rebuild();
        dp(0);
        printf("%d
    ",f[0][m]);
    }
    /*
    g++ 1.cpp -o 1
    ./1
    4 15
    3 2 5 5
    3 2 10 7
    3 1 2 1
    */
    View Code
    Zeit und Raum trennen dich und mich.时空将你我分开。
  • 相关阅读:
    服务器日常维护
    每日哲言
    JAVA经典算法40题(原题+分析)之分析
    如何在苹果笔记本上装win7系统
    酷派改变者S1(C105/C105-6/C105-8) 解锁BootLoader 并刷入recovery root
    努比亚 Z17 mini s (Nubia NX589J) 解锁BootLoader 并刷入recovery ROOT
    努比亚 Z17s (Nubia NX595J) 解锁BootLoader 并刷入recovery ROOT
    努比亚 N2(Nubia NX575J) 解锁BootLoader 并进入临时recovery ROOT
    三星A3、A5、A7、G7、J5、J7、S6系列等新机型的部分手机解锁 ROOT刷机
    [2月1号] 努比亚全机型ROM贴 最全最新NubiaUI5.0 ROOT 极速体验
  • 原文地址:https://www.cnblogs.com/starsing/p/11179296.html
Copyright © 2011-2022 走看看