1. 程式人生 > >[HAOI2010]軟體安裝 ---拓撲排序 +樹型dp

[HAOI2010]軟體安裝 ---拓撲排序 +樹型dp

傳送門洛谷 P2515


題目描述

現在我們的手頭有N個軟體,對於一個軟體i,它要佔用Wi的磁碟空間,它的價值為Vi。我們希望從中選擇一些軟體安裝到一臺磁碟容量為M計算機上,使得這些軟體的價值儘可能大(即Vi的和最大)。

但是現在有個問題:軟體之間存在依賴關係,即軟體i只有在安裝了軟體j(包括軟體j的直接或間接依賴)的情況下才能正確工作(軟體i依賴軟體j)。幸運的是,一個軟體最多依賴另外一個軟體。如果一個軟體不能正常工作,那麼它能夠發揮的作用為0。

我們現在知道了軟體之間的依賴關係:軟體i依賴軟體Di。現在請你設計出一種方案,安裝價值儘量大的軟體。一個軟體只能被安裝一次,如果一個軟體沒有依賴則Di=0,這時只要這個軟體安裝了,它就能正常工作。


分析

看起來像是一道裸的樹型分組揹包來著,結果…
其實按照題目的描述,應該是由內向樹(基環樹的一種)和普通樹組成的森林(如下圖),因此要考慮先將森林處理成一棵樹,把環縮成點,再進行dp。
在這裡插入圖片描述
話說網上的都要用tarjan縮點呢,直接拓撲找環不就行了麼。
dp的話算是模板了吧,分組揹包罷了,參考選課吧。

程式碼

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>

#define IL inline

using namespace std;
IL int read() { char c = getchar(); int sum = 0 ,k = 1; for(;'0' > c || c > '9'; c = getchar()) if(c == '-') k = -1; for(;'0' <= c && c <= '9'; c = getchar()) sum = sum * 10 + c - '0'; return sum * k; } int fa[105]; int col[105]; int degree[105]; int val[105
], weight[105]; int to[205], nxt[205]; int cnt, last[105]; IL void add(int u, int v) { //printf("%d %d\n", u, v); to[++cnt] = v; nxt[cnt] = last[u]; last[u] = cnt; to[++cnt] = u; nxt[cnt] = last[v]; last[v] = cnt; } int f[105][505]; int n, m, S; IL void topsort() { queue<int>Q; for(int i = 1; i <= n; ++i) if(!degree[i]) Q.push(i); for(int u; !Q.empty();) { u = Q.front(); Q.pop(); if(fa[u] && !(--degree[fa[u]])) Q.push(fa[u]); } } IL void build() { S = n + 1; bool flag = 0; for(int i = 1; i <= n; ++i) if(degree[i]) { degree[i] = 0; for(int x = fa[i]; x != i; x = fa[x]) { --degree[x]; val[i] += val[x]; weight[i] += weight[x]; col[x] = i; } fa[i] = 0; if(!flag) flag = 1; } for(int i = 1; i <= n; ++i) if(col[i] == i) { if(fa[i]) add(col[fa[i]], i); else add(S, i); } } IL int max_(int x, int y) { return x > y ? x : y; } IL void dfs(int u, int pre) { f[u][weight[u]] = val[u]; for(int i = last[u], v; (v = to[i]); i = nxt[i]) if(v != pre) { dfs(v, u); for(int j = m; j >= weight[u]; --j) for(int k = 0; j - k >= weight[u]; ++k) f[u][j] = max_(f[u][j], f[u][j - k] + f[v][k]); } } int main() { n = read(); m = read(); for(int i = 1; i <= n; ++i) weight[i] = read(); for(int i = 1; i <= n; ++i) { col[i] = i; val[i] = read(); } for(int i = 1; i <= n; ++i) { fa[i] = read(); if(fa[i]) ++degree[fa[i]];} topsort(); build(); dfs(S, 0); printf("%d\n", f[S][m]); return 0; }