洛谷 P2515 [HAOI2010]軟體安裝
洛谷 P2515 [HAOI2010]軟體安裝
題目描述
現在我們的手頭有NN個軟體,對於一個軟體i,它要佔用W_iW**i的磁碟空間,它的價值為V_iV**i。我們希望從中選擇一些軟體安裝到一臺磁碟容量為MM計算機上,使得這些軟體的價值儘可能大(即V_iV**i的和最大)。
但是現在有個問題:軟體之間存在依賴關係,即軟體i只有在安裝了軟體jj(包括軟體j的直接或間接依賴)的情況下才能正確工作(軟體ii依賴軟體jj)。幸運的是,一個軟體最多依賴另外一個軟體。如果一個軟體不能正常工作,那麼它能夠發揮的作用為00。
我們現在知道了軟體之間的依賴關係:軟體i依賴軟體D_iD**i。現在請你設計出一種方案,安裝價值儘量大的軟體。一個軟體只能被安裝一次,如果一個軟體沒有依賴則D_i=0D**i
輸入格式
第1行:N,M(0\leq N\leq 100, 0\leq M\leq 500)N,M(0≤N≤100,0≤M≤500)
第2行:W_1,W_2, ... W_i, ..., W_n (0\leq W_i\leq M)W1,W2,...W**i,...,W**n(0≤W**i≤M)
第3行:V_1, V_2, ..., V_i, ..., V_n (0\leq V_i\leq 1000)V1,V2,...,V**i,...,V**n(0≤V**i≤1000)
第4行:D_1, D_2, ..., D_i, ..., D_n (0\leq D_i\leq N, D_i≠i)D1,D2,...,D**i
輸出格式
一個整數,代表最大價值
題解:
2020.11.14模擬賽T4 10pts場。。。
蒟蒻太菜啦!
明明考場上想到了是縮點,但是不會拍Tarjan了...蒟蒻太菜啦
所以只拿了10pts的全裸揹包暴力。
其實如果DAG給些分,還能多拿。
所以簡單講一下思路:
首先能發現的性質是:對於一個有向環,選就得全選,不選就都不能選。所以這就是一個大號的點。
所以要Tarjan等效縮點啊。然後再建虛節點0把所有連通塊連一起。就可以樹形DP了。
程式碼:
#include <iostream> #include <cstdlib> #include <cstdio> #include <algorithm> using namespace std; const int maxn = 505; int n, m, cnt, w[maxn], a[maxn], d[maxn]; int dfn[maxn], low[maxn], bel[maxn], tot, scc, ins[maxn], sta[maxn], top; int W[maxn], V[maxn], indag[maxn], dp[maxn][maxn]; struct edge { int v; edge *next; }pool[maxn * 2], *head[maxn]; inline void add(int u, int v) { edge *p = &pool[++cnt]; p->v = v, p->next = head[u], head[u] = p; } void tarjan(int u) { dfn[u] = low[u] = ++tot; sta[++top] = u; ins[u] = 1; for(edge *p = head[u]; p; p = p->next) { int v = p->v; if(!dfn[v]) { tarjan(v); low[u] = min(low[u], low[v]); } else if(ins[v]) low[u] = min(low[u], dfn[v]); } if(dfn[u] == low[u]) { ++scc; while(sta[top + 1] != u) { bel[sta[top]] = scc; W[scc] += w[sta[top]]; V[scc] += a[sta[top]]; ins[sta[top--]] = 0; } } } void solve(int u) { for(int i = W[u]; i <= m; i++) dp[u][i] = V[u]; for(edge *p = head[u]; p; p = p->next) { int v = p->v; solve(v); int k = m - W[u]; for(int i = k; i >= 0; i--) for(int j = 0; j <= i; j++) dp[u][i + W[u]] = max(dp[u][i + W[u]], dp[v][j] + dp[u][i + W[u] - j]); } } 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", &a[i]); for(int i = 1; i <= n; i++) { scanf("%d", &d[i]); if(d[i]) add(d[i], i); } for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i); for(int i = 0; i <= n; i++) head[i] = 0; cnt = 0; for(int i = 1; i <= n; i++) if(bel[d[i]] != bel[i]) { add(bel[d[i]], bel[i]); indag[bel[i]]++; } for(int i = 1; i <= scc; i++) if(!indag[i]) add(0, i); solve(0); printf("%d\n", dp[0][m]); return 0; }