樹形dp就是....我也不知道是什麼,反正乙個主件下面有很多的附件可選就是樹形dp,咕咕咕
樹形dp的主要實現形式是dfs,在dfs中dp,主要的實現形式是dp[i]j,i是以i為根的子樹,j是表示在以i為根的子樹中選擇j個子節點,0表示這個節點不選,1表示選擇這個節點。有的時候j或0/1這一維可以壓掉
選擇節點類:
\[dp[i][0]=dp[j][1]
dp[i][1]=max/min(dp[j][0],dp[j][1])\]
樹形揹包類:
\[dp[v][k]=dp[u][k]+val
dp[u][k]=max(dp[u][k],dp[v][k−1])\]
其實我覺得就是這樣的:
dp[i][j]並不表示以i為根,選j個物品,而是從根節點到i的路徑及其左邊所有的節點,以及以i為根的子樹的所有節點中,容量為j的最大價值。上述方程應該用到了泛化物品的方法
定義考慮這樣一種物品,它並沒有固定的費用和價值,而是它的價值隨著你分配給它的費用而變化。這就是泛化物品的概念。
更嚴格的定義之。在揹包容量為v的揹包問題中,泛化物品是乙個定義域為0..v中的整數的函式h,當分配給它的費用為v時,能得到的價值就是h(v)。
這個定義有一點點抽象,另一種理解是乙個泛化物品就是乙個陣列h[0..v],給它費用v,可得到價值h[v]。
乙個費用為c價值為w的物品,如果它是01揹包中的物品,那麼把它看成泛化物品,它就是除了h(c)=w其它函式值都為0的乙個函式。如果它是完全揹包中的物品,那麼它可以看成這樣乙個函式,僅當v被c整除時有h(v)=v/cw,其它函式值均為0。如果它是多重揹包中重複次數最多為n的物品,那麼它對應的泛化物品的函式有h(v)=v/cw僅當v被c整除且v/c<=n,其它情況函式值均為0。
乙個物品組可以看作乙個泛化物品h。對於乙個0..v中的v,若物品組中不存在費用為v的的物品,則h(v)=0,否則h(v)為所有費用為v的物品的最大價值。p07中每個主件及其附件集合等價於乙個物品組,自然也可看作乙個泛化物品。
泛化物品的和
如果面對兩個泛化物品h和l,要用給定的費用從這兩個泛化物品中得到最大的價值,怎麼求呢?事實上,對於乙個給定的費用v,只需列舉將這個費用如何分配給兩個泛化物品就可以了。同樣的,對於0..v的每乙個整數v,可以求得費用v分配到h和l中的最大價值f(v)。也即
f(v)=max
可以看到,f也是乙個由泛化物品h和l決定的定義域為0..v的函式,也就是說,f是乙個由泛化物品h和l決定的泛化物品。
由此可以定義泛化物品的和:h、l都是泛化物品,若泛化物品f滿足以上關係式,則稱f是h與l的和。這個運算的時間複雜度取決於揹包的容量,是o(v^2)。
泛化物品的定義表明:在乙個揹包問題中,若將兩個泛化物品代以它們的和,不影響問題的答案。事實上,對於其中的物品都是泛化物品的揹包問題,求它的答案的過程也就是求所有這些泛化物品之和的過程。設此和為s,則答案就是s[0..v]中的最大值。
揹包問題的泛化物品
乙個揹包問題中,可能會給出很多條件,包括每種物品的費用、價值等屬性,物品之間的分組、依賴等關係等。但肯定能將問題對應於某個泛化物品。也就是說,給定了所有條件以後,就可以對每個非負整數v求得:若揹包容量為v,將物品裝入揹包可得到的最大價值是多少,這可以認為是定義在非負整數集上的一件泛化物品。這個泛化物品——或者說問題所對應的乙個定義域為非負整數的函式——包含了關於問題本身的高度濃縮的資訊。一般而言,求得這個泛化物品的乙個子域(例如0..v)的值之後,就可以根據這個函式的取值得到揹包問題的最終答案。
綜上所述,一般而言,求解揹包問題,即求解這個問題所對應的乙個函式,即該問題的泛化物品。而求解某個泛化物品的一種方法就是將它表示為若干泛化物品的和然後求之。(以上內容取自揹包九講)
上圖取自國家集訓隊**《**幾類揹包題》,點選可以放大.
傳送門題目意思是想選擇一門課,必須要先學會它的必修課,即選擇一門課必須要選擇必修課。那麼他又說要選擇的價值最大,這就是揹包問題啦。又因為他有依賴性性行為,所以就是個樹形dp
解釋一下樣例:
很明顯此題的關聯關係一目了然,所以我們可以在子樹上適用範化揹包.
注意爸爸是0說明沒有爸爸
即:設f[i][j]表示選擇以i為根的子樹中j個節點。
u代表當前根節點,sum代表其選擇的節點的總額。
#include#include#include#include#include#include#include#include#include#include#define ll long long int
#define maxn 2500
using namespace std;
inline int read()
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}struct node e[maxn];
int n,m,head[maxn],c[maxn],f[maxn][maxn],sum;
void add(int x,int y)
void build(int u,int t)
if(a==0)
}build(0,m);
cout傳送門
這道題比上面的簡單多了,咕咕咕,就是直接用第乙個dp方程
#include#include#include#include#include#include#include#include#include#include#define ll long long int
#define maxn 6666
using namespace std;
const int maxn=999999999;
const int minn=-999999999;
inline int read()
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;}/*
注釋掉的有bug
struct node e[maxn];
void add(int x,int y)
void do_it(int x)
}int main()
int zero1,zero2;//讀掉0
cin>>zero1>>zero2;
for(int i=1; i<=n; ++i)
} do_it(root);
cout<>n;
for(int i=1; i<=n; i++)
cin>>dp[i][1];
for(int i=1; i<=n; i++)
for(int i=1; i<=n; i++)
if(!ind[i])
do_it(root);
cout<
}
學習筆記 樹形dp
最近幾天學了一下樹形 dp 其實早就學過了 來提高一下開啟樹形 dp 的姿勢。1 沒有上司的晚會 我的人生第一道樹形 dp 其實就是兩種情況 dp i 1 表示第i個人來時的最大人數 dp i 0 表示第i個人不來時的最大人數 然後遞迴至葉子節點,倒推 dp 狀態轉移方程 dp root 1 dp ...
樹形DP學習筆記
1.為什麼會有樹形dp 正常來說,線性dp用來解決序列的問題,但是當我們維護的資料結構發生變化的時候,比如,現在我們需要對一棵樹 進行dp,普通的線性dp邊無法解決了,這個時候,就需要用到樹形dp了 2.樹形dp的應用場景 1中的應用場景給的比較抽象,這裡詳細的來說一下。最經典的例子就是乙個等級森嚴...
樹形dp學習
練習專題參考 傳送門 hdu 1520 題意 給出n個點,然後給出n個點對應的歡樂值,然後給出n 1條邊,a b,表示b是a的直屬上級,現在舉行乙個patry,但是要求員工和他的直屬上級不能同時來,問來的人的歡樂值的最大和是多少 思路 首先明確這是一棵有向樹,dp i 0 1 代表第i個人來 不來的...