BZOJ2427 软件安装 : Tarjan缩点 + 树形背包

BZOJ2427 软件安装 : Tarjan缩点 + 树形背包

Description

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

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

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

Input

第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 )

Output

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

Sample Input

3 10
5 5 6
2 3 4
0 1 1

Sample Output

5

HINT
题解

这是一个有依赖的背包问题,但题目中依赖关系可能会形成环,就代表如果要取这个物品就得把这个强连通分量的所有物品给取完。
所以可以把其缩成一个物品,再重新建立依赖关系,从被依赖指向依赖,用依赖背包求解即可。
其中f[i][j]代表再以i个物品为根时,体积为j时的最优方案。

#include <cstdio>
#include <iostream>
#include <cmath>
#include <stack>
#include <algorithm>
#include <cstring>
#include <climits>
#include<vector>
#define MAXN 100+10
#define MAXM 500+10
using namespace std;
struct Edge{
    int next,from,to,tnext;
};
struct G{
    int head[MAXN],num,tail[MAXN];
    Edge edge[MAXM*2];
    void add(int from,int to)
    {
        edge[++num].next=head[from];
        edge[num].tnext=tail[to];
        edge[num].from=from;
        edge[num].to=to;
        head[from]=num;tail[to]=num;
    }
}g1,g2;
struct Data{
    int w,v;
}k[MAXN];
int v[MAXN],w[MAXN],f[MAXN][MAXM];
int n,m;
int low[MAXN],dfn[MAXN],dfnum,cnt[MAXN],col[MAXN],co,vis[MAXN],vis2[MAXN];
int ans[MAXN],ans2[MAXN],maxn;
stack<int> st;
vector<int> con[MAXN];
void tarjan(int x)
{
    dfn[x]=low[x]=++dfnum;vis[x]=1;st.push(x);
    for(int i=g1.head[x];i;i=g1.edge[i].next)
    {
        if(!dfn[g1.edge[i].to])
        {
            tarjan(g1.edge[i].to);
            low[x]=min(low[x],low[g1.edge[i].to]);
        }else if(vis[g1.edge[i].to]) low[x]=min(low[x],dfn[g1.edge[i].to]);
    }
    if(low[x]==dfn[x])
    {
        int t=1,v1=0,w1=0;col[x]=++co;vis[x]=0;
        con[co].push_back(x);
        while(st.top()!=x) 
        {
            con[co].push_back(st.top());t++;
            vis[st.top()]=0;
            col[st.top()]=co;
            v1+=v[st.top()];
            w1+=w[st.top()];
            st.pop();
        }
        v1+=v[st.top()];
        w1+=w[st.top()];
        st.pop();cnt[co]=t;
        k[co].w=w1;k[co].v=v1;
    }
}
int ru[MAXN];
void dp(int x)
{
    for(int i=g2.head[x];i;i=g2.edge[i].next)
    {
        dp(g2.edge[i].to);
        for(int j=m-k[x].w;j>=0;j--)
            for(int l=0;l<=j;l++)    //l代表给x的子树g2.edge[i].to分配j-l空间时最优方案。
                f[x][j]=max(f[x][j],f[x][l]+f[g2.edge[i].to][j-l]);
    for(int i=m;i>=0;i--)
    {
        if(i>=k[x].w) f[x][i]=f[x][i-k[x].w]+k[x].v;
        else  f[x][i]=0;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    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++) 
    {
        int x;scanf("%d",&x);
        if(x)  g1.add(x,i);
    }
    for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
    for(int i=1;i<=n;i++)
        if(col[g1.edge[i].from]!=col[g1.edge[i].to])
            g2.add(col[g1.edge[i].from],col[g1.edge[i].to]),
            ru[col[g1.edge[i].to]]++;
    for(int i=1;i<=co;i++)
        if(ru[i]==0) g2.add(co+1,i);
    k[co+1].v=k[co+1].w=0;
    dp(co+1);
    printf("%d\n",f[co+1][m]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值