zoukankan      html  css  js  c++  java
  • 【bzoj2427】[HAOI2010]软件安装 Tarjan+树形背包dp

    题目描述

    现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi。我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和最大)。
    但是现在有个问题:软件之间存在依赖关系,即软件i只有在安装了软件j(包括软件j的直接或间接依赖)的情况下才能正确工作(软件i依赖软件j)。幸运的是,一个软件最多依赖另外一个软件。如果一个软件不能正常工作,那么它能够发挥的作用为0。
    我们现在知道了软件之间的依赖关系:软件i依赖软件Di。现在请你设计出一种方案,安装价值尽量大的软件。一个软件只能被安装一次,如果一个软件没有依赖则Di=0,这时只要这个软件安装了,它就能正常工作。

    输入

    第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 )

    输出

    一个整数,代表最大价值。

    样例输入

    3 10
    5 5 6
    2 3 4
    0 1 1

    样例输出

    5


    题解

    Tarjan+树形背包dp

    一开始刚看题以为是基环树背包dp,不过仔细想想可以发现一个环当且仅当全部选择才可以发挥作用。于是可以先把所有的环缩点,这里直接上了Tarjan的板子。

    然后原图就变成了一个森林,使用树形背包的方法来处理。设$f[i][j]$表示在以$i$为根的子树中选择根和其它符合条件的节点,使得总体积为$j$时可以获得的最大价值。可以枚举子节点的贡献进行状态转移。

    对于每棵树求出这个值以后,再把每棵树的所有结果做一次dp即可。

    时间复杂度$O(nm^2)$。

    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    #define N 110
    using namespace std;
    vector<int> e[N] , c[N];
    int m , w[N] , v[N] , deep[N] , low[N] , tot , vis[N] , ins[N] , sta[N] , top , num , bl[N] , a[N] , b[N] , fa[N] , f[N][510] , g[510];
    void tarjan(int x)
    {
    	int i;
    	deep[x] = low[x] = ++tot , vis[x] = ins[x] = 1 , sta[++top] = x;
    	for(i = 0 ; i < (int)e[x].size() ; i ++ )
    	{
    		if(!vis[e[x][i]]) tarjan(e[x][i]) , low[x] = min(low[x] , low[e[x][i]]);
    		else if(ins[e[x][i]]) low[x] = min(low[x] , deep[e[x][i]]);
    	}
    	if(deep[x] == low[x])
    	{
    		int t;
    		num ++ ;
    		do
    		{
    			t = sta[top -- ];
    			ins[t] = 0 , bl[t] = num; 
    		}
    		while(t != x);
    	}
    }
    void dfs(int x)
    {
    	int i , j , k;
    	for(i = 0 ; i < a[x] ; i ++ ) f[x][i] = -1 << 29;
    	for(i = a[x] ; i <= m ; i ++ ) f[x][i] = b[x];
    	for(i = 0 ; i < (int)c[x].size() ; i ++ )
    	{
    		dfs(c[x][i]);
    		for(j = m ; j >= 0 ; j -- )
    			for(k = j ; k >= 0 ; k -- )
    				f[x][j] = max(f[x][j] , f[x][j - k] + f[c[x][i]][k]);
    	}
    }
    int main()
    {
    	int n , i , j , k , d;
    	scanf("%d%d" , &n , &m);
    	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &w[i]);
    	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &v[i]);
    	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &d) , e[d].push_back(i);
    	for(i = 1 ; i <= n ; i ++ )
    		if(!vis[i])
    			tarjan(i);
    	for(i = 1 ; i <= n ; i ++ )
    	{
    		a[bl[i]] += w[i] , b[bl[i]] += v[i];
    		for(j = 0 ; j < (int)e[i].size() ; j ++ )
    			if(bl[i] != bl[e[i][j]])
    				fa[bl[e[i][j]]] = bl[i] , c[bl[i]].push_back(bl[e[i][j]]);
    	}
    	for(i = 1 ; i <= num ; i ++ )
    	{
    		if(!fa[i])
    		{
    			dfs(i);
    			for(j = m ; j >= 0 ; j -- )
    				for(k = j ; k >= 0 ; k -- )
    					g[j] = max(g[j] , g[j - k] + f[i][k]);
    		}
    	}
    	printf("%d
    " , g[m]);
    	return 0;
    }
    

     

  • 相关阅读:
    K&R C C90,C99的改进
    Windows 用来定位 DLL 的搜索路径
    常量字符串的问题
    C++0x中一些有用的新特性
    mainfest机制
    mainfest机制
    C++0x中一些有用的新特性
    c语言目标程序中的段
    c语言目标程序中的段
    数据模型(LP32 ILP32 LP64 LLP64 ILP64 )
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7481291.html
Copyright © 2011-2022 走看看