HAOI2010 軟體安裝 縮點 樹形DP

2021-08-22 15:16:59 字數 2955 閱讀 9802

【題目描述】

現在我們的手頭有n個軟體,對於乙個軟體i,它要占用wi的磁碟空間,它的價值為vi。我們希望從中選擇一些軟體安裝到一台磁碟容量為m計算機上,使得這些軟體的價值盡可能大(即vi的和最大)。

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

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

【輸入格式】

第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 )

【輸出格式】

乙個整數,代表最大價值。

【樣例輸入】

3 10

5 5 6

2 3 4

0 1 1

【樣例輸出】

5【題意分析】

給你一堆軟體,每個軟體有重量和價值,而且有乙個依賴物件,裝了依賴物件才能裝當前這個。

蒟蒻剛拿到這道題——這不裸的樹上揹包動規嗎?於是暴力從沒有依賴的軟體(看做根節點)打了個樹形dp,還過樣例了。

於是自信滿滿地點了一下標籤——縮點??!!

恍然大悟——哦!題目沒說這是一棵樹。。。

因此可能會存在多個環。我們想一下——乙個環裡的軟體肯定是要麼全裝,要麼全不裝。那麼我們就把乙個環打包起來。。。成乙個節點。剩下的就是乙個dag圖(有向無環圖),在這個dag圖上進行樹形動規。

那麼這些環將何去何從?把它們連到那裡去?可以想一下,一旦撞到環了,肯定就不能往更深的地方下去了,因為它們忙著互相依賴,哪有功夫再往深的地方鑽?那麼我們就用簡單暴力點的方法:弄乙個虛擬節點0,把環和森林的根節點全都連到虛擬節點上面去。然後從虛擬節點開始樹形dp。

樹上揹包問題:設dp[

i][j

dp[i][j

dp[i][

j]表示目前到i節點,花費j容量獲得的最大價值。那麼dp[

i][j

+w[i

]]=m

ax(d

p[i]

[j+w

[i]]

,dp[

i][j

+w[i

]−k]

+dp[

v][k

]dp[i][j+w[i]]=max(dp[i][j+w[i]],dp[i][j+w[i]-k]+dp[v][k]

dp[i][

j+w[

i]]=

max(

dp[i

][j+

w[i]

],dp

[i][

j+w[

i]−k

]+dp

[v][

k]其中w[i]是當前這個節點的重量,v是列舉的每個子節點,k取遍0到j。

重新建圖的時候原來的東西要清零~

code:

#include#include#include#include#include#define max 1000

using namespace std;

struct front_link_staredge[max];

stack s;

int head[max],w[max],v[max],w[max],v[max],dag[max];

int dfn[max],low[max],indegree[max],n,m,tot_circle,cnt,tag;

int dp[max][max];

bool vis[max],check[max][max];

inline void add_edge(int u,int v)

inline int read()

while (isdigit(ch))

return s*w;

}inline void tarjan(int now)else if (vis[v])low[now]=min(low[now],low[v]);

} if (dfn[now]==low[now])while (now!=y);

}} //縮點

inline void dp(int now) //暴力dp

}int main()

for (register int i=1;i<=n;i++)

if (!dfn[i])tarjan(i); //縮點

for (register int i=1;i<=n;i++) //判斷是否是環。

}}

//重新建圖。

//本來可以直接add_edge(i,j),因為會對圖造成影響,所以弄乙個判斷陣列。

cnt=0;

memset(edge,0,sizeof(edge));

memset(head,0,sizeof(head));

//清零別忘了。

for (register int i=1;i<=tot_circle;i++)

for (register int j=1;j<=tot_circle;j++)

if (check[i][j])add_edge(i,j);

for (register int i=1;i<=tot_circle;i++)

if (!indegree[i])add_edge(0,i);

//瘋狂連邊

dp(0); //從虛擬節點開始dp

printf("%d",dp[0][m]); //dp[0][m]為答案

return 0;

}

HAOI2010 軟體安裝

現在我們的手頭有n個軟體,對於乙個軟體i,它要占用wi的磁碟空間,它的價值為vi。我們希望從中選擇一些軟體安裝到一台磁碟容量為m計算機上,使得這些軟體的價值盡可能大 即vi的和最大 但是現在有個問題 軟體之間存在依賴關係,即軟體i只有在安裝了軟體j 包括軟體j的直接或間接依賴 的情況下才能正確工作 ...

HAOI2010 軟體安裝

現在我們的手頭有n個軟體,對於乙個軟體i,它要占用wi的磁碟空間,它的價值為vi。我們希望從中選擇一些軟體安裝到一台磁碟容量為m計算機上,使得這些軟體的價值盡可能大 即vi的和最大 但是現在有個問題 軟體之間存在依賴關係,即軟體i只有在安裝了軟體j 包括軟體j的直接或間接依賴 的情況下才能正確工作 ...

HAOI2010 軟體安裝

開始沒有看懂題,以為就是個樹形依賴揹包,打完之後w40,然後才發現它會有還,要用tarjan縮完點後跑揹包,要建立乙個虛擬節點0連線所有的子圖 注意連線的位置 錯誤示範 1 for int i 1 i n i 這樣會導致0連線的不是入度為0的點 或環 2if dsn i 3 正確 1 for int...