zoukankan      html  css  js  c++  java
  • 【bzoj2427】【软件安装】tarjan缩点+树形依赖背包

    这里写图片描述
    (上不了p站我要死了,侵权度娘背锅)

    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

    wa了好久啊。。。在一些很细节的地方犯了很多低级错误
    逐字比对之后才终于调出来,这wa率蹭蹭蹭就上去了。。T_T

    其实这道题还是比较基础的。就是tarjan缩点之后,将所有树连向一个“超级根”,跑树上dp。

    这个dp呢,是一个树形依赖背包。虽说是依赖背包,处理过后其实可以看做是一个分组背包。即,假设我们已处理出子树v占用m内存的最大价值,我们便可将子树v的各个值看做一个组,只能选择其中一种内存大小。而这道题的分组背包已经算是泛化物品了,外层for当前节点u的内存(从大到小),内层for子树v的内存大小(随便顺序)。
    时间复杂度是o(n*m*m),可过

    然后要附上wa的地方
    看来是算法模板没有熟练,才会导致粗心的低级错误

    完整代码(附wa点)

    #include<stack>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int N=205;
    const int M=1005;
    
    stack<int> s;
    int n,m,ww[N],vv[N];
    int/*adde1*/ head1[N],end1[N*2],nxt1[N*2],hh1=0;
    int/*adde2*/ head2[N],end2[N*2],nxt2[N*2],hh2=0;
    int/*tarjan*/ dfn[N],low[N],idc=0,id[N],cnt=0;bool exi[N];
    int cos[N],val[N],in[N];
    int/*dfs*/ dp[N][M];
    
    void adde1(int a,int b){
        hh1++;
        end1[hh1]=b;
        nxt1[hh1]=head1[a];
        head1[a]=hh1;
    }
    void adde2(int a,int b){
        hh2++;
        end2[hh2]=b;
        nxt2[hh2]=head2[a];
        head2[a]=hh2;
    }
    void tarjan(int u){
        dfn[u]=low[u]=++idc;
        s.push(u);
        exi[u]=1;
        for(int i=head1[u];i;i=nxt1[i]){
            int v=end1[i];
            if(!dfn[v]){
                tarjan(v);
                low[u]=min(low[u],low[v]);
            }
            else if(exi[v]) low[u]=min(low[u],low[v]);
        }
        if(low[u]==dfn[u]){
            cnt++;
            int tmp;
            while(s.top()!=u&&(!s.empty())){
                tmp=s.top();s.pop();
                id[tmp]=cnt;
                exi[tmp]=0;
            }
            if(s.top()==u){
                s.pop();
                id[u]=cnt;
                exi[u]=0;//最后在处理u时忘了。。
            }
        }
    }
    void dfs(int u){//printf("%d ",u);
        dp[u][0]=0;
        for(int i=head2[u];i;i=nxt2[i]){
            int v=end2[i];
            dfs(v);
            for(int k=m-cos[u];k>=0;k--){//用了两次i,就导致了混乱
                for(int j=k;j>=0;j--){
                    dp[u][k]=max(dp[u][k],dp[u][k-j]+dp[v][j]);
                }
            }
        }
        for(int j=m;j>=0;j--){
            if(j-cos[u]>=0) dp[u][j]=dp[u][j-cos[u]]+val[u];
            else dp[u][j]=0;
        }
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%d",&ww[i]);
        for(int i=1;i<=n;i++) scanf("%d",&vv[i]);
        int d;
        for(int i=1;i<=n;i++){
            scanf("%d",&d);
            if(d) adde1(d,i);
        }
        while(!s.empty()) s.pop();
        for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
        for(int u=1;u<=n;u++){
            cos[id[u]]+=ww[u],val[id[u]]+=vv[u];
            for(int i=head1[u];i;i=nxt1[i]){
                int v=end1[i];
                if(id[u]!=id[v]) adde2(id[u],id[v]),in[id[v]]++;
            }
        }
        for(int i=1;i<=cnt;i++) if(!in[i]) adde2(0,i);
        memset(dp,-27,sizeof(dp));
        dfs(0);
        int ans=0;
        for(int i=1;i<=m;i++) ans=max(ans,dp[0][i]);
        printf("%d
    ",ans);
        return 0;
    }

    总结:
    1、像tarjan这种模板类型的要烂熟于心

  • 相关阅读:
    HashMap 常问的 9 个问题
    P1855 榨取kkksc03
    Codeforces Round #697 (Div. 3) A. Odd Divisor
    P1474 [USACO2.3]Money System / [USACO07OCT]Cow Cash G
    Codeforces Round #704 (Div. 2) D. Genius's Gambit
    P2800 又上锁妖塔
    P2066 机器分配
    P3399 丝绸之路
    P1351 [NOIP2014 提高组] 联合权值
    P4290 [HAOI2008]玩具取名
  • 原文地址:https://www.cnblogs.com/LinnBlanc/p/7763101.html
Copyright © 2011-2022 走看看