• 洛谷 P2515 [HAOI2010]软件安装 解题报告


    P2515 [HAOI2010]软件安装

    题目描述

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

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

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

    输入输出格式

    输入格式:

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

    第2行:(W_1,W_2,...W_i,...,W_n) ((0<=W_i<=M))

    第3行:(V_1, V_2, ..., V_i, ..., V_n) ((0<=V_i<=1000))

    第4行:(D_1, D_2, ..., D_i, ..., D_n) ((0<=D_i<=N, D_i≠i))

    输出格式:

    一个整数,代表最大价值


    咋一看十分的像背包,但里面的东西互相拧成一坨相互依赖之类的,我们需要稍微简化一下问题。

    一个软件最多依赖另外一个软件,把被别人依赖的某个软件向依赖它的软件连上一条有向边,可以得出,每个点的入度均为1,这是啥?一棵树啊。

    然而这样想就出现了问题,万一有环呢?好说,把环给缩掉就行了。我们把新出现的一个森林连上一个共同的虚根0,构成一颗树,于是问题就转换成了树形DP

    需要注意的是,一个点要用它子树,当且仅当这个子树的根被选上时才可用。

    (dp[i][j])代表以(i)为根的树,在容量为(j)的时候,没有处理它的根,所得到的最大价值。

    转移:(dp[i][j]=max(dp[i][j],dp[i-k][j]+dp[son][k-w[son]]+v[son]))


    #include <cstdio>
    int max(int x,int y){return x>y?x:y;}
    int min(int x,int y){return x<y?x:y;}
    const int N=104;
    const int M=502;
    struct Edge
    {
        int to,next;
    }edge[N];
    int head[N],cnt=0;
    void add(int u,int v)
    {
        edge[++cnt].next=head[u];edge[cnt].to=v;head[u]=cnt;
    }
    int n,m,w[N],v[N];
    int time=0,low[N],dfn[N],vis[N],s[N],tot=0,ha[N];
    int n0=0,w0[N],v0[N],in[N],g[N][N];
    void tarjan(int now)
    {
        low[now]=dfn[now]=++time;
        s[++tot]=now;
        vis[now]=1;
        for(int i=head[now];i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(!dfn[v])
            {
                tarjan(v);
                low[now]=min(low[now],low[v]);
            }
            else if(vis[v])
                low[now]=min(low[now],dfn[v]);
        }
        if(dfn[now]==low[now])
        {
            int k;n0++;
            do
            {
                k=s[tot--];
                vis[k]=0;
                ha[k]=n0;
                w0[n0]+=w[k];
                v0[n0]+=v[k];
            }while(k!=now);
        }
    }
    int dp[N][M];//以i为根(不装)的子树装j时的最大价值
    void dfs(int now)
    {
        for(int i=1;i<=n0;i++)
            if(g[now][i])
            {
                dfs(i);
                for(int j=m;j>=w0[i];j--)
                    for(int k=w0[i];k<=j;k++)
                        dp[now][j]=max(dp[now][j],dp[i][k-w0[i]]+v0[i]+dp[now][j-k]);
            }
    }
    int main()
    {
        scanf("%d%d",&n,&m);int to;
        for(int i=1;i<=n;i++) scanf("%d",w+i);
        for(int i=1;i<=n;i++) scanf("%d",v+i);
        for(int i=1;i<=n;i++) {scanf("%d",&to);if(to) add(to,i);}
        for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
        for(int i=1;i<=n;i++)
            for(int j=head[i];j;j=edge[j].next)
            {
                int v=edge[j].to;
                if(ha[i]!=ha[v]&&!g[ha[i]][ha[v]])
                    g[ha[i]][ha[v]]=1,in[ha[v]]++;
            }
        for(int i=1;i<=n0;i++)
            if(!in[i]) g[0][i]=1;;
        dfs(0);
        printf("%d
    ",dp[0][m]);
        return 0;
    }
    
    

    2018.6.13

  • 相关阅读:
    System.InvalidOperationException异常的处理!vs15版
    五子棋项目的实现(四)具体的总结
    五子棋项目的实现(三)人机对战类的具体设计
    五子棋项目的实现(二)博弈树算法的描述
    五子棋项目的实现(一)
    java Exception和Error的区别
    java内存分配实例
    expdp与impdp
    SQL*PLUS下使用AUTOTRACE、sql顾问、实时sql监控
    Linux下如何将数据库脚本文件从sh格式变为sql格式
  • 原文地址:https://www.cnblogs.com/butterflydew/p/9178122.html
Copyright © 2020-2023  润新知