zoukankan      html  css  js  c++  java
  • bzoj2427:[HAOI2010]软件安装(Tarjan+tree_dp)

    2427: [HAOI2010]软件安装

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 1053  Solved: 424
    [Submit][Status][Discuss]

    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

     

    Source

    Day2

    /*
    开始一看这不是个基础的树型动规吗?(知道基础,但我不会啊),但是一看还有环嘞,苦逼了... 
    根据依赖关系可以画出来一张图,有三种可能的情况:
    1.依赖关系构成一棵树。 
    2.依赖关系构成一个环。 
    3.依赖关系构成一个环下面吊着一棵树。
    因为有2,3这些情况,所以要先有tarjan预处理一下,缩环为点,重新建图。
    对于建好的图,跑一边树形背包即可,思想类似于01背包
    f[x][tot]表示以x为根,容量为tot的最大收益。把x的各个子树看成物品
    再枚举每个子树所分给的容量,tot从大到小转移。
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    using namespace std;
    int n,m,cnt,scc,ind,top,num;
    int v[105],w[105];
    int sv[105],sw[105];bool in_stack[105];
    int dfn[105],low[105],belong[105];
    int stack[105],f[105][505],in[505],head[505],head2[505];
    struct node
    {
        int from;
        int to;
        int next;
    }e[1010],e2[1010];
    
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    
    inline void insert(int from,int to)
    {
        e[++num].from=from;
        e[num].to=to;
        e[num].next=head[from];
        head[from]=num;
    }
    
    inline void insert2(int from,int to)
    {
        in[to]=1;
        e2[++num].from=from;
        e2[num].to=to;
        e2[num].next=head2[from];
        head2[from]=num; 
    }
    
    void Tarjan(int u)
    {
        int now=0;
        dfn[u]=low[u]=++ind;
        stack[++top]=u;
        in_stack[u]=1;
        for(int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if(!dfn[v])
            {
                Tarjan(v);
                low[u]=min(low[u],low[v]);
            }
            else if(in_stack[v]) low[u]=min(low[u],dfn[v]);
        }
        if(low[u]==dfn[u])
        {
            scc++;
            while(now!=u)//统计环 
            {
                now=stack[top--];in_stack[now]=0;
                belong[now]=scc;
                sw[scc]+=w[now];
                sv[scc]+=v[now];
            }
        }
    }
    void rebuild()//重建图 
    {
        num=0;
        for(int x=1;x<=n;x++)
          for(int i=head[x];i;i=e[i].next)
          {
              int v=e[i].to;
              if(belong[v]!=belong[x])
                insert2(belong[x],belong[v]);
          }
    }
    void dp(int x)
    {
        for(int i=head2[x];i;i=e2[i].next)
        {
            dp(e2[i].to);
            for(int j=m-sw[x];j>=0;j--) 
            {
                for(int k=0;k<=j;k++)//枚举子树的的限制。
                    f[x][j]=max(f[x][j],f[x][k]+f[e2[i].to][j-k]);        
            }
        }
        for(int j=m;j>=0;j--)//完全背包 
        {
            if(j>=sw[x])f[x][j]=f[x][j-sw[x]]+sv[x];
            else f[x][j]=0;
        }
    }
    
    int main()
    {
        n=read();m=read();
        for(int i=1;i<=n;i++)w[i]=read();
        for(int i=1;i<=n;i++)v[i]=read();
        for(int i=1;i<=n;i++)
        {
            int x=read();
            if(x)insert(x,i);
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i])Tarjan(i);
        rebuild();
        for(int i=1;i<=scc;i++)
            if(!in[i])
                insert2(scc+1,i);//这个地方要加1,因为根节点属于新的环。(不确定) 
        dp(scc+1);
        printf("%d
    ",f[scc+1][m]);
        return 0;
    } 
    折花枝,恨花枝,准拟花开人共卮,开时人去时。 怕相思,已相思,轮到相思没处辞,眉间露一丝。
  • 相关阅读:
    linux下面安装maven
    go 安装
    linux scp 服务器远程拷贝
    git有merge时如何删除分支
    influxdb ERR: error parsing query: found -, expected
    influxDB学习总结
    influxdb Measurements
    go exec: "gcc": executable file not found in %PATH%
    php计算脚本执行时间
    安装nodejs和grunt以后出现 /usr/bin/env: node: No such file or directory
  • 原文地址:https://www.cnblogs.com/L-Memory/p/6375878.html
Copyright © 2011-2022 走看看