洛谷 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

原文地址:https://www.cnblogs.com/butterflydew/p/9178122.html