2427: [HAOI2010]软件安装
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1150 Solved: 449
[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
5 5 6
2 3 4
0 1 1
Sample Output
HINT
Source
tarjan+背包dp 调了好长时间...
我们很容易发现一个道理:如果我们选了一个东西,那么它直到它这棵树的根上的东西都要选。而且它们之间的关系可以形成环,如果我们选了环上任意一个东西,那么整个环也的选。
所以我们可以把一个环缩成一个点,幸运的是这是一个基环树森林,我们用tarjan缩点就行了。
然后进行树上dp... 我们发现这个东西很像一个背包问题,就是我们当前有m单位空间,用多少空间在一些限制下可以获得最大利益。这个限制就是如果我们选了一个东西,它到它的根上的东西都得选。
缩完点后是一个森林,于是我们加入一个虚拟顶点作为所有树的根,这样就很方便了。于是我们设定状态dp(i,j)当前选到了i节点,用了j单位空间,所能获得的最大价值。注意,这里的i节点是必须选的,聪明的读者们应该已经知道了。
于是我们这样转移:先转移每个儿子的状态:dp(i,j) = max(dp(i,j), dp(i,k) + dp(son(i),j - k)))这不难想清楚 但是注意j和k要倒序枚举 因为这里的更新用到了前面的东西,所以要倒序更新。
最后我们要加上自己的花费: dp(i,j) = dp(i, j - w[i])+v[i] 如果j<w[i]那么dp(i,j)=0,一定要清零,因为这个状态是不合法的。
#include <bits/stdc++.h> using namespace std; const int N = 1010; struct edge { int nxt, to; } e[N]; struct point { int w, v; } x[N]; vector<int> g[N]; int n, m, cnt = 1, tot, top, scc; int head[N], node[N], in[N], dfn[N], low[N], st[N]; int dp[N][N], w[N], v[N], vis[N]; void link(int u, int v) { e[++cnt].nxt = head[u]; head[u] = cnt; e[cnt].to = v; } void tarjan(int u) { dfn[u] = low[u] = ++ tot; st[++ top] = u; vis[u] = 1; for(int i = 0; i < g[u].size(); ++i) { int v = g[u][i]; if(!dfn[v]) { tarjan(v); low[u] = min(low[u], low[v]); } else if(vis[v]) low[u] = min(low[u], dfn[v]); } if(dfn[u] == low[u]) { ++scc; int t = 0; while(t != u) { t = st[top--]; x[scc].w += w[t]; x[scc].v += v[t]; vis[t] = 0; node[t] = scc; } } } void dfs(int u) { for(int i = head[u]; i; i = e[i].nxt) { dfs(e[i].to); for(int j = m - x[u].w; j >= 0 ; --j) for(int k = j; k >= 0; --k) dp[u][j] = max(dp[u][j], dp[u][k] + dp[e[i].to][j - k]); } for(int i = m; i >= 0; --i) if(i >= x[u].w) dp[u][i] = dp[u][i - x[u].w] + x[u].v; else dp[u][i] = 0; } int main() { scanf("%d%d", &n, &m); for(int i = 1; i <= n; ++i) scanf("%d", &w[i]); for(int i = 1; i <= n; ++i) scanf("%d", &v[i]); for(int i = 1; i <= n; ++i) { int u; scanf("%d", &u); if(u) g[u].push_back(i); } for(int i = 1; i <= n; ++i) if(!dfn[i]) tarjan(i); for(int i = 1; i <= n; ++i) for(int j = 0; j < g[i].size(); ++j) { int v = g[i][j]; if(node[i] != node[v]) { link(node[i], node[v]); ++in[node[v]]; } } ++scc; for(int i = 1; i < scc; ++i) if(!in[i]) link(scc, i); dfs(scc); printf("%d ", dp[scc][m]); return 0; }