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

    题目类型:(tarjan)缩点+树形(dp)

    传送门:>Here<

    题意:给出N各节点,每个节点是一个软件,该软件有占用空间(W[i])和价值(V[i])。软件之间有依赖关系,如果想要运行(i),就必须安装(d[i])。问总空间不超过(M)时,运行的最大价值

    解题思路

    首先读题要仔细——安装和运行在这里是两个概念。安装相当于去选择一个软件集合,而在选择完所有的以后才能开始运行。我们可以选择建图,从(d[i])(i)连边。通过读题不难发现所有节点的入度都不超过1。因此对于每一个连通块,要么是一个环,要么是一个(DAG)。而对于一个环,要么全部安装并运行,要么全部不安装(不可能安装一部分的环)。因此自然而然的,我们可以把环看做一个点,因此首先进行缩点。

    缩点完了之后,整张图就变成了一个多个连通块的(DAG)。由于不存在环,每个连通块有且仅有存在入度为0的点。从一个虚拟根节点像所有入度为0的点连边,就形成了一个联通的(DAG)——由于入度不超过1,可以将其看做一棵树。

    于是问题转化为树上的01背包,前提是要选子树的话必须选择该子树的根节点。

    考虑树形背包的做法(这一点非常关键)。一般我们都令(dp[u][j])表示以(u)为根节点的子树中,总空间设定为(j)时的最大价值。然而这样的定义经常使人受到误导——这让人觉得很难转移。

    实际上我们定义的原型应该是(dp[u][i][j])表示以(u)为根节点的子树中,前(i)棵子树中,总空间设定为(j)时的最大价值。因此我们有状态转移方程$$dp[u][i][j]=Max{dp[u][i-1][j],dp[u][i-1][j-k]+dp[v][all][k]}$$那为什么在普通的定义中没有(i)呢?因为(i)已经通过滚动数组优化了

    这个方程的实质是爆扫——枚举当前子树分配到的空间,剩下的空间给之前的。但是这里有一个特殊的点,就是根节点必须选。这就意味着(dp[u][i-1][j-k])必须包含根节点,其实质也就是(j-k geq W[u])。这样每次转移的时候不至于让根节点消失了。

    反思

    读题是非常重要的。不误解题目意思是做出这道题的关键。当然考虑如何必须选根节点来推方程这方面好像还没什么经验,对于(dp)还是要多加练习啊。

    Code

    注意这是一个01背包。由于我们采用滚动数组优化,无论这是不是在树上,外层的(j)都是需要倒扫的。(当前一轮取决于上一轮比自己小的,正扫就会覆盖)

    /*By DennyQi 2018*/
    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    const int MAXN = 110;
    const int MAXM = 510;
    const int INF = 1061109567;
    inline int Max(const int a, const int b){ return (a > b) ? a : b; }
    inline int Min(const int a, const int b){ return (a < b) ? a : b; }
    inline int read(){
        int x = 0; int w = 1; register char c = getchar();
        for(; c ^ '-' && (c < '0' || c > '9'); c = getchar());
        if(c == '-') w = -1, c = getchar();
        for(; c >= '0' && c <= '9'; c = getchar()) x = (x<<3) + (x<<1) + c - '0'; return x * w;
    }
    int N,M,dfs_clock,top;
    int w[MAXN],v[MAXN],d[MAXN];
    int _first[MAXN],_nxt[MAXM],_to[MAXM],_cnt;
    int dfn[MAXN],low[MAXN],sta[MAXN],sccno[MAXN],scc_cnt;
    int W[MAXN],V[MAXN],rd[MAXN];
    int first[MAXN],nxt[MAXN],to[MAXN],cnt;
    int dp[MAXN][50010];
    inline void _add(int u, int v){
    	_to[++_cnt] = v, _nxt[_cnt] = _first[u], _first[u] = _cnt;
    }
    inline void add(int u, int v){
    	to[++cnt] = v, nxt[cnt] = first[u], first[u] = cnt;
    }
    void tarjan(int u){
    	int _v;
    	dfn[u] = low[u] = ++dfs_clock;
    	sta[++top] = u;
    	for(int i = _first[u]; i; i = _nxt[i]){
    		_v = _to[i];
    		if(!dfn[_v]){
    			tarjan(_v);
    			low[u] = Min(low[u], low[_v]);
    		}
    		else if(!sccno[_v]){
    			low[u] = Min(low[u], dfn[_v]);
    		}
    	}
    	if(low[u] == dfn[u]){
    		++scc_cnt;
    		while(1){
    			sccno[sta[top]] = scc_cnt;
    			W[scc_cnt] += w[sta[top]];
    			V[scc_cnt] += v[sta[top]];
    			--top;
    			if(sta[top+1] == u){
    				break;
    			}
    		}
    	}
    }
    void DP(int u){
    	for(int j = W[u]; j <= M; ++j){
    		dp[u][j] = V[u];
    	}
    	int v;
    	for(int i = first[u]; i; i = nxt[i]){
    		v = to[i];
    		DP(v);
    		for(int j = M; j >= W[u]; --j){
    			for(int k = 0; k <= j-W[u]; ++k){
    				dp[u][j] = Max(dp[u][j], dp[u][j-k] + dp[v][k]);
    			}
    		}	
    	}
    	
    }
    int main(){
    //	freopen(".in","r",stdin);
    	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){
    		d[i] = read();
    		if(d[i]){
    			_add(d[i], i);
    		}
    	}
    	for(int i = 1; i <= N; ++i){
    		if(!dfn[i]){
    			tarjan(i);
    		}
    	}
    	for(int i = 1; i <= N; ++i){
    		if(sccno[i] != sccno[d[i]] && d[i]){
    			add(sccno[d[i]], sccno[i]);
    			++rd[sccno[i]];
    		}
    	}
    	for(int i = 1; i <= scc_cnt; ++i){
    		if(rd[i] == 0){
    			add(N+1, i);
    		}
    	}
    	DP(N+1);
    	printf("%d", dp[N+1][M]);
    	return 0;
    }
    
  • 相关阅读:
    聊聊自定义实现的SPI如何与spring进行整合
    聊聊读源码这件事
    聊聊自定义SPI如何使用自定义标签注入到spring容器中
    聊聊如何自定义数据脱敏
    聊聊自定义SPI如何与sentinel整合实现熔断限流
    排查not eligible for getting processed by all BeanPostProcessors
    SqlServer 数据脱敏脚本
    WPF 窗体使用 Show() 单个显示并设置弹窗相对于主窗体位置
    【SQLite】获取插入 ID 的几种方式
    Winform DataGridView 行单元格增加自定义 ToolTip
  • 原文地址:https://www.cnblogs.com/qixingzhi/p/9854269.html
Copyright © 2011-2022 走看看