zoukankan      html  css  js  c++  java
  • BZOJ 2427: [HAOI2010]软件安装

    Time Limit: 10 Sec Memory Limit: 128 MB
    Submit: 2146 Solved: 865
    [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

    解题思路

    比较典型的树上背包问题,但是有个细节是图不一定是联通的,也就是说可能出现环,
    思考环,如果选其中的一个就必须都选,所以要用tarjan缩点。之后就是一个树上背包问题了,设dp[x][i]为以x为节点的子树占了i 的空间的最大收益,

            for(register int j=m;j>=W[x];j--)  //x的空间
                for(register int k=0;k<=j-W[x];k++){ //子树的空间
                    dp[x][j]=max(dp[x][j],dp[u][k]+dp[x][j-k]);

    注意第二个循环要从0到j-W[x],因为选u的前提是选x,所以给u所能用的空间最大只能为j-W[x]。最后答案为dp[0][m]。

    考试的时候tarjan里连了双向边。。。。尴尬的60了。还是因为太菜。

    代码

    
    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    
    using namespace std;
    const int MAXN = 505;
    
    inline int rd(){
        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<<1)+(x<<3)+ch-48;ch=getchar();}
        return x*f;
    }
    
    int n,m,w[MAXN],val[MAXN],W[MAXN],V[MAXN];
    int head[MAXN],cnt;
    int dfn[MAXN],low[MAXN],xx[MAXN],head_[MAXN],cnt_;
    int sta[MAXN],top,num;
    int col[MAXN],col_num,du[MAXN];
    int dp[MAXN][MAXN],ans;
    bool vis[MAXN];
            //dp[x][i] 表示以x为节点的子树,用了i的空间,最大权值 
    struct Edge{
        int nxt,to;
    }edge[MAXN<<1],edge_[MAXN<<1];
    
    inline void add(int bg,int ed){
        edge[++cnt].to=ed;
        edge[cnt].nxt=head[bg];
        head[bg]=cnt;
    }
    
    inline void add_(int bg,int ed){
        edge_[++cnt_].to=ed;
        edge_[cnt_].nxt=head_[bg];
        head_[bg]=cnt_;
    }
    
    inline void tarjan(int x){
        dfn[x]=low[x]=++num;
        vis[x]=1;
        sta[++top]=x;
        for(register int i=head[x];i;i=edge[i].nxt){
            int u=edge[i].to;
            if(!dfn[u]){
                tarjan(u);
                low[x]=min(low[x],low[u]);
            }
            else if(vis[u])
                low[x]=min(low[x],dfn[u]);
        }
        if(low[x]==dfn[x]){
            vis[x]=0;
            col_num++;
            while(sta[top]!=x){
                col[sta[top]]=col_num;
                vis[sta[top]]=0;
                top--;
            }
            top--;
            col[x]=col_num;
        }
    }
    
    inline void dfs(int x){
        for(register int i=W[x];i<=m;i++) dp[x][i]=V[x];
        for(register int i=head_[x];i;i=edge_[i].nxt){
            int u=edge_[i].to;
            dfs(u);
            for(register int j=m;j>=W[x];j--)
                for(register int k=0;k<=j-W[x];k++){
                    dp[x][j]=max(dp[x][j],dp[u][k]+dp[x][j-k]);
                }
        }   
    
    }
    
    int main(){
    //  freopen("software.in","r",stdin);
    //  freopen("software.out","w",stdout);
        n=rd();m=rd();
        for(register int i=1;i<=n;i++) w[i]=rd();
        for(register int i=1;i<=n;i++) val[i]=rd();
        for(register int i=1;i<=n;i++){
            xx[i]=rd();
            add(xx[i],i);
        }
        for(register int i=1;i<=n;i++) 
            if(!dfn[i]) tarjan(i);
    //  for(register int i=0;i<=n;i++) cout<<col[i]<<" ";cout<<endl;
        col[0]=0;
        for(register int i=1;i<=n;i++){
            W[col[i]]+=w[i];
            V[col[i]]+=val[i];
            if(col[i]==col[xx[i]]) continue;
            add_(col[xx[i]],col[i]);
            du[col[i]]++;
    //      if(col[xx[i]]==col[0]) mark[col[i]]=1;
        }
        for(register int i=1;i<=col_num;i++)
            if(!du[i]) add_(col[0],i);
    //  for(register int i=1;i<=col_num;i++) {
    //      if(i!=col[0])
    //      add(i,col[0]);
    //  }
    //  for(register int i=1;i<=col_num;i++) cout<<W[i]<<" "<<V[i]<<endl;
        dfs(0);
    //  for(register int i=0;i<=col_num;i++)
    //      for(register int j=0;j<=m;j++)
    //          cout<<i<<" "<<j<<" "<<dp[i][j]<<endl;
        ans=dp[0][m];
        printf("%d",ans);
        return 0;
    }
    
    /*
    3 10
    5 5 6 
    2 3 4 
    0 1 1
    */
    
    /*
    5 7
    4 2 1 2 3
    5 3 50 6 19
    0 1 2 2 1
    */
    
    /*
    7 14
    5 1 6 6 6 1 1
    1 2 1 2 1 5 4
    0 1 2 2 1 5 5
    */
    
    /*
    7 23
    4 4 4 4 1 4 4 
    100 100 100 100 1 5 4
    4 1 2 3 0 5 5
    */
  • 相关阅读:
    Javascript 使用字符串
    JavaScript精简学习4(动态表单和链接处理)
    JavaScript 使用表单
    事半功倍之Javascript (2)
    jQuery隐藏按钮
    .NET线程同步之Interlocked和ReadWrite 锁线程同步——事件构造
    linux命令汇总
    好的设计能减少大量的工作
    Prism学习笔记
    编写C函数的技巧
  • 原文地址:https://www.cnblogs.com/sdfzsyq/p/9676992.html
Copyright © 2011-2022 走看看