【HAOI 2010】软件安装

版权声明:转载请注明出处。 https://blog.csdn.net/TungstenC/article/details/83175892

题目描述

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

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

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

0 N 100 0\le N\le 100 0 M 500 0\le M\le 500 0 W i M 0\le W_i\le M 0 V i 1000 0\le V_i\le 1000 0 D i N 0\le D_i\le N D i i D_i≠i

算法分析

与金明的预算方案不同的是,这里的依赖关系可能会形成一个环,跑一边 Tarjan 算法缩环,如果我们选择了这个环中的一个节点,那么为了获得价值,该环内的每个点都必选,因此可以看成一个整体,缩环后整个图变成了一棵树,计算树上有依赖背包 DP 即可。

其实可以像 【JSOI 2016】最佳团体 那样进一步缩小枚举范围,但是这题不卡常,就懒得写了。

第一次写的时候居然先清空图再跑了 Tarjan,in[] 数组的必要性在于新图的根节点处可能是一个环。

代码实现

#include <cstdio>
#include <cstring>
#include <algorithm>
const int maxn=105;
const int maxm=505;
int head[maxn],ev[maxn],nxt[maxn],idx=0;
inline void add(int u,int v) {ev[++idx]=v;nxt[idx]=head[u];head[u]=idx;}
int dfn[maxn],low[maxn],dfnidx=0,sta[maxn],top=0;
int sccno[maxn],sccidx=0;
void tarjan(int x) {
    dfn[x]=low[x]=++dfnidx;sta[top++]=x;
    for(int i=head[x];i;i=nxt[i]) {
        int v=ev[i];
        if(!dfn[v]) {
            tarjan(v);
            low[x]=std::min(low[x],low[v]);
        }
        else if(!sccno[v]) low[x]=std::min(low[x],dfn[v]);
    }
    if(dfn[x]==low[x]) {
        ++sccidx;
        while(top) {
            int u=sta[--top];
            sccno[u]=sccidx;
            if(x==u) break;
        }
    }
}
int n,m,W[maxn],V[maxn],d[maxn];
int w[maxn],v[maxn],in[maxn],f[maxn][maxm];
void dfs(int x) {
    for(int i=0;i<=m;++i) f[x][i]=-0x3f3f3f3f;
    f[x][0]=0;if(w[x]<=m) f[x][w[x]]=v[x];
    for(int i=head[x];i;i=nxt[i]) {
        int v=ev[i];dfs(v);
        for(int j=m;j>=0;--j) {
            for(int k=0;k<=j-w[x];++k) {
                f[x][j]=std::max(f[x][j],f[x][j-k]+f[v][k]);
            }
        }
    }
}
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) {
        scanf("%d",&d[i]);
        if(d[i]) add(d[i],i);
    }
    for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
    memset(head,0,sizeof(head));idx=0;sccno[0]=0;
    for(int i=1;i<=n;++i) {
        w[sccno[i]]+=W[i];v[sccno[i]]+=V[i];
        if(d[i]&&sccno[d[i]]!=sccno[i]) {
            add(sccno[d[i]],sccno[i]);
            ++in[sccno[i]];
        }
    }
    for(int i=1;i<=sccidx;++i) if(!in[i]) add(0,i);
    dfs(0);
    int ans=-0x3f3f3f3f;
    for(int i=0;i<=m;++i) ans=std::max(ans,f[0][i]);
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/TungstenC/article/details/83175892
今日推荐