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

    BZOJ2427: [HAOI2010]软件安装

    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

    题解Here!

    最近沉迷$DP$无法自拔。。。
    这是一个有依赖的背包问题。
    强行$Tarjan$缩点然后转换成树形$DP$。
    缩点应该不用多说。
    然后来看树上怎么做。
    设$dp[i][j]$表示当前在$i$这个点,用了$j$个单位的磁盘空间,所能获得的最大价值。
    转移方程就很好写啦:
    设$weight[i]$表示$i$这个点的磁盘空间,$value[i]$表示$i$这个点的价值。
    首先对于$DFS$到的所有的$u$,我们初始化:
    $$dp[u][i]=value[u],iin[weight[u],m]$$
    对于$forall vin son_u$,我们这样转移:
    $$dp[u][i+weight[u]]=max{ dp[v][j]+dp[u][i+weight[u]-j] | iin[0,m-weight[u]],jin[0,i] }$$
    其中,$i$为倒序枚举,$j$为正序枚举。
    这就是树形背包全过程。
    但是!缩完点之后有可能不止一棵树啊!
    不要紧,我们建立一个超级树根$root$,连到每棵树的树根即可。
    最终答案就是$dp[root][m]$。
    然而一开始把缩点敲炸了,尴尬。。。
    附代码:
    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #define MAXN 110
    using namespace std;
    int n,m,root;
    int weight[MAXN],value[MAXN],colour[MAXN];
    inline int read(){
        int date=0,w=1;char c=0;
        while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
        while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
        return date*w;
    }
    namespace Tarjan{//强行namespace,缩点
        int c=1,d=1,top=1,s=0;
        int cstack[MAXN],head[MAXN],deep[MAXN],low[MAXN],w[MAXN],v[MAXN],fa[MAXN];
        bool vis[MAXN];
        struct Edge{
            int next,to;
        }a[MAXN];
        inline void add_edge(int x,int y){
            a[c].to=y;a[c].next=head[x];head[x]=c++;
        }
        void dfs(int x){
            deep[x]=low[x]=d++;
            vis[x]=true;
            cstack[top++]=x;
            for(int i=head[x];i;i=a[i].next){
                int v=a[i].to;
                if(!deep[v]){
                    dfs(v);
                    low[x]=min(low[x],low[v]);
                }
                else if(vis[v])low[x]=min(low[x],deep[v]);
            }
            if(low[x]==deep[x]){
                s++;
                do{
                    colour[cstack[top-1]]=s;
                    vis[cstack[top-1]]=false;
                }while(cstack[--top]!=x);
            }
        }
        void solve(){
            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++){
                fa[i]=read();
                if(fa[i])add_edge(fa[i],i);
            }
            for(int i=1;i<=n;i++)if(!deep[i])dfs(i);
            for(int i=1;i<=n;i++){
                weight[colour[i]]+=w[i];
                value[colour[i]]+=v[i];
            }
        }
    }
    namespace DP{//树形背包
    	int c=1;
    	int head[MAXN],deep[MAXN],indegree[MAXN],dp[MAXN][MAXN*5];
    	struct Tree{
    		int next,to;
    	}a[MAXN<<2];
    	inline void add_edge(int x,int y){
    		a[c].to=y;a[c].next=head[x];head[x]=c++;
    	}
    	void dfs(int rt){
    		int will;
    		for(int i=weight[rt];i<=m;i++)dp[rt][i]=value[rt];
    		for(int i=head[rt];i;i=a[i].next){
    			will=a[i].to;
    			if(!deep[will]){
    				deep[will]=deep[rt]+1;
    				dfs(will);
    				for(int j=m-weight[rt];j>=0;j--){
    					for(int k=0;k<=j;k++){
    						dp[rt][j+weight[rt]]=max(dp[rt][j+weight[rt]],dp[rt][j+weight[rt]-k]+dp[will][k]);
    					}
    				}
    			}
    		}
    	}
    	void solve(){
    		root=n+1;
    		weight[root]=value[root]=0;
    		for(int i=1;i<=n;i++){
    			int v=Tarjan::fa[i];
    			if(!v)continue;
    			if(colour[i]!=colour[v]){
    				add_edge(colour[v],colour[i]);
    				indegree[colour[i]]++;
    			}
    		}
    		for(int i=1;i<=Tarjan::s;i++)if(!indegree[i])add_edge(root,i);
    		deep[root]=1;
    		dfs(root);
    	}
    }
    void work(){
        DP::solve();
        printf("%d
    ",DP::dp[root][m]);
    }
    void init(){
        n=read();m=read();
        Tarjan::solve();
    }
    int main(){
        init();
        work();
        return 0;
    }
    
  • 相关阅读:
    微信公众平台入门到精通-新浪云计算平台注册和使用
    PhpStorm快捷键设置/个性化设置,如何多项目共存?如何更换主题?
    10个免费的PHP编辑器/开发工具
    php利用PHPExcel类导出导入Excel用法
    PHP遍历数组的几种方法
    SQLSTATE[HY000] [2003] Can't connect to MySQL server on 'XXX' (13)
    CentOS下Apache虚拟主机配置
    centos下vsftpd登录后无法看见文件,无法创建文件及文件夹
    Linux开启MySQL远程连接
    php禁止某ip或ip地址段访问的方法(转载)
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9885625.html
Copyright © 2011-2022 走看看