深入理解樹形揹包

2021-10-04 20:44:08 字數 1765 閱讀 2470

【題目描述】p1273 【有線電視網】

某收費有線電視網計畫轉播一場重要的足球比賽。他們的轉播網和使用者終端構成一棵樹狀結構,這棵樹的根結點位於足球比賽的現場,樹葉為各個使用者終端,其他中轉站為該樹的內部節點。

從轉播站到轉播站以及從轉播站到所有使用者終端的訊號傳輸費用都是已知的,一場轉播的總費用等於傳輸訊號的費用總和。

現在每個使用者都準備了一筆費用想**這場精彩的足球比賽,有線電視網有權決定給哪些使用者提供訊號而不給哪些使用者提供訊號。

寫乙個程式找出乙個方案使得有線電視網在不虧本的情況下使**轉播的使用者盡可能多。

【題目分析】

這是乙個非常不錯的樹形揹包的題目,有助於理解樹形揹包的實質! 按照常見樹形揹包定義狀態:設dp[u][j]表示在以u為根的子樹中,選擇j個客戶所能獲得的最大收益。

狀態轉移:dp[u][j]=max(dp[u][j-k],dp[v][k]-w(u,v));

樹形揹包一直有個疑惑,為什麼在列舉揹包容量的時候要反向列舉,淺層次的理解就是套用01揹包列舉體積;但是細想又不對,因為01揹包只有在使用滾動陣列的時候才必須逆推;如果使用2維的話不需要;

那是不是意味著樹形揹包中原本應該是3維,滾動優化為2維了呢???

仔細回味01揹包的狀態定義:f[i][j]表示在前i個物品占用體積為j的空間能夠得到的最大價值;這裡有乙個限定前i個,初次學習樹形揹包一看人家的**感覺就是這麼寫的,並沒有深入去思考為什麼。

所以仿照01揹包定義dp[i][u][j]表示在以u為根的子樹的前i棵子樹中選擇j個節點能夠獲得的最大費用

dp[i][u][j]=max(dp[i-1][u][j-k]+dp[v的總兒子數][v][k]-w(u,v))

以上就是樹形dp沒有滾動優化的狀態方程,為什麼需要滾動優化?為什麼可以滾動優化?

為什麼需要滾動優化?————不滾動很可能會mle

為什麼可以滾動優化???

1.對於u而言很顯然是可以滾動優化的,只不過j-k2.關鍵在於v,滾動優化後怎麼保證每次dp[v][k]==dp[v的總兒子數][v][k]? 其實關鍵還是在於01揹包的本質的理解,dp[i][v][j]從v的前i個子樹中選擇j個節點,遞推求解的時候是i依次增大的,所以結束時一定有"i==v的總兒子數" 因此使用滾動陣列計算v結束後,dp[v][k]中一定是儲存的從v的所有兒子中選k個節點的值;

從而保證dp[v][k]==dp[v的總兒子數][v][k]

#includeusing namespace std;

const int n=3001;

struct node

} g[n];

int fir[n],cnt,money[n],n,m,dp[n][n];

void add(int f,int t,int w)

int dfs(int u)

dp[u][0]=0;//不選任何節點一定收益為0

int leafs=0;//統計以u為根的子樹中葉子的數目

for(int i=fir[u];i;i=g[i].nxt)

return leafs;

}int main()

}for(int j=n-m+1;j<=n;j++)

cin>>money[j];

memset(dp,0x8f,sizeof(dp));

memset(dp,0,sizeof(dp[0]));

dfs(1);

for(int i=m;i>=0;i--)

} return 0;

}

揹包九講的深入理解

01揹包問題 簡述 有n件物品,容量為v的揹包,每一件物品的容量為c i 價值為 w i 問如何裝物品 才能使在不超過揹包容量的前提下,價值最大。計 dp i j 表示最大裝i個物品,此時揹包的記憶體為j時的價值大小,很顯然,j v dp i j max dp i 1 j dp i 1 j c i ...

深入理解閉包

閉包的定義 mdn 對閉包的定義 根據上面的例子,舉乙個例子 var str xiaoqi function getname getname 函式可以返回str這個變數,但str即不是getname函式的區域性變數,也不是foo函式的引數,所以str就是自由變數。這樣函式getname就是乙個閉包。...

深入理解JavaScript閉包

一 什麼是閉包 多個 兩個或兩個以上 函式巢狀,當內部函式被儲存到外部時,將會生成閉包。內部函式在外面執行的時候一定能夠呼叫的了原來它在的那個函式環境裡的變數。閉包會導致原有作用域鏈不釋放,造成記憶體洩露。functiona var aaa 123 return b var glob 100 var...