[HAOI2010]軟體安裝 ---拓撲排序 +樹型dp
阿新 • • 發佈:2018-11-10
傳送門:洛谷 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;
}