软件安装 [HAOI2010,Bzoj2427]

69 篇文章 0 订阅
2 篇文章 0 订阅

题目地址请点击——


软件安装


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,Dii


Output

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


Sample Input

3 10
5 5 6
2 3 4
0 1 1


Sample Output

5


Solution

首先,可能有环。
所以我们要先缩点。我用的是 Tarjan 求强连通分量。
然后就是一个经典的树形依赖背包问题, O(nm) 即可解决。
推荐资料:徐持衡的论文《浅谈几类背包问题》。


Code

#include <iostream>
#include <cstdio>
#include <cstring>

#define Min(x,y) ((x)<(y)?(x):(y))
#define Max(x,y) ((x)>(y)?(x):(y))

using namespace std;

int n,m,cnt,top,tot,tott,front,cnt2;
int score[50010],w[50010],w1[50100],v1[50010],f[5100][2010],dfn[50100],stack[05010];
int head[10010],nxt[10000],data[10000],belong[10000],pre[10000],queue[10000];
int head1[10010],nxt1[10010],data1[10010];
bool vissss[50010],vis[50010],in_stack[50010],can[1000][1000],pres[1000];

void add(int x,int y){
    nxt[cnt]=head[x];data[cnt]=y;head[x]=cnt++;
}

void add2(int x,int y){
    nxt1[cnt2]=head1[x];data1[cnt2]=y;head1[x]=cnt2++;
}

void dfs(int s,int v){
    if(v<=0)return;
    for(int i=head1[s];i!=-1;i=nxt1[i]){
        int d=data1[i];
        for(int j=0;j<=v-w1[d];j++)f[d][j]=f[s][j]+v1[d];
        dfs(d,v-w1[d]);
        for(int j=w1[d];j<=v;j++){
            if(f[s][j]<f[d][j-w1[d]])
                f[s][j]=f[d][j-w1[d]];
        }
    }
}

void Tarjan(int x){
    pre[x]=dfn[x]=++tot;
    in_stack[x]=true;
    stack[++top]=x;
    for(int i=head[x];i!=-1;i=nxt[i]){
        if(!dfn[data[i]]){
            Tarjan(data[i]);
            pre[x]=Min(pre[x],pre[data[i]]);
        }
        else if(in_stack[data[i]])pre[x]=Min(pre[x],dfn[data[i]]);
    }
    if(dfn[x]==pre[x]){
        tott++;
        belong[x]=tott;
        int j;
        do{
            j=stack[top--];
            in_stack[j]=false;
            belong[j]=tott;
            w1[tott]+=w[j];
            v1[tott]+=score[j];
        }while(j!=x);
    }
}

void bfs(int ss){
    queue[++front]=ss;
    while(front){
        int now=queue[front--];
        for(int i=head[now];i!=-1;i=nxt[i]){
            if(belong[now]!=belong[data[i]]&&!can[belong[now]][belong[data[i]]]){
                can[belong[now]][belong[data[i]]]=can[belong[data[i]]][belong[now]]=true;
                add2(belong[now],belong[data[i]]);
                pres[belong[data[i]]]=true;
            }
            if(!vis[data[i]]){
                queue[++front]=data[i];
                vis[data[i]]=false;
            }
        }
    }
}

inline void in(int &y){
    y=0;
    char x=getchar();
    while(x<'0'||x>'9')x=getchar();
    while(x>='0'&&x<='9'){y=y*10+x-'0';x=getchar();}
}

int main(){

    memset(head,-1,sizeof head);
    memset(head1,-1,sizeof head1);

    in(n);in(m);

    for(int i=1;i<=n;i++)in(w[i]);
    for(int i=1;i<=n;i++)in(score[i]);
    for(int i=1;i<=n;i++){
        int x;
        in(x);
        add(x,i);
    }

    for(int i=0;i<=n;i++)if(!belong[i])Tarjan(i);

    for(int i=1;i<=n;i++)if(!vis[i]){
        vis[i]=true;
        bfs(i);
    }

    for(int i=1;i<=tott;i++)if(!pres[i]&&i!=belong[0]){
        add2(belong[0],i);
    }

    dfs(belong[0],m);
    printf("%d\n",f[belong[0]][m]);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值