樹形揹包學習筆記

2022-05-07 19:57:16 字數 2529 閱讀 1844

給定一棵有$n$個節點的點權樹,要求你從中選出$m$個節點,使得這些選出的節點的點權和最大,乙個節點能被選當且僅當其父親節點被選中,根節點可以直接選。

考慮設$f[u][i]$表示在$u$的子樹中選擇$i$個節點(包括它本身)的最大貢獻,則可列出以下轉移方程。

$$f[u][i]=max(f[u][j]+f[v][i-j]+d[v])\ [j=1...i-1]

$$其中$d[v]$表示點$v$的點權,$i-j$表示在子樹$v$中選擇$i-j$個節點。

由於遍歷整棵樹是$\theta(n)$的,而選取$i$和$j$是$o(m2)$的,所以整個程式的複雜度就是$o(nm2)$的。

luogu p2014 選課

這是一道樹形揹包的模板題,可以將題目轉化為在$n+1$個節點中選$m+1$個節點。於是最後的答案就是$f[0][m+1]$。

#include #include using std::max;

const int n = 3e2 + 10, m = 3e2 + 10;

int n, m, f[n][n], s[n], son[n][n];

void dfs (int u)

}int main ()

dfs(0);

printf ("%d\n", f[0][m + 1]);

return 0;

}

顯然,$n^3$演算法的時間開銷是很$big$的,比如這道題:洛谷 p4322 最佳團體。

此題在$01$分數規劃後採取樹形揹包$check$,但是,$nm^2log$的時間複雜度是不允許,考慮優化樹形揹包的$check$過程

首先,既然要優化,我們就得知道瓶頸在哪。瓶頸在於,我們是一邊$dfs$一邊更新的,由於要遍歷子樹,我們同時還要知道選擇多少個節點,那麼我們是否可以先跑一遍$dfs$處理出$dfs$序然後根據$dfs$序,來更新。

設$f[i][j]$為當前$dp$到$dfs$序為$i$的點,目前已經選了$j$個節點。則有轉移方程($d[i]$表示點權):

1.選取當前節點:

$$

f[i+1][j+1]=f[i][j]+d[i]

$$如果選了這個點,則在$dfs$序後乙個節點要麼是它的子節點,要麼下一棵子樹(則證明其沒有子節點)。

2.不選當前節點:

$$

f[nx[i]][j]=f[i][j]

$$其中$nx[i]$表示下一棵子樹,因為你沒選這個點,當然不能選擇其子節點。

由於$dfs$序為$\theta(n)$的,然後列舉$j$為$o(m)$的,所以總複雜度為$o(nmlog)$。

同樣是luogu p2014 選課

#include #include using std::min;

typedef long long ll;

const int n = 3e2 + 10, m = 3e2 + 10, inf = 1e9 + 7;

int n, m, d[n], s[n], dfn[n], son[n][n], time, f[n][n], nx[n];

inline void upt (int &a, int b)

void init_dfs(int u)

void doit_dp()

}int main ()

init_dfs(0);//預處理dfs

doit_dp();//動態規劃

printf("%d\n", f[n + 1][m]);

return 0;

}

之前我們提到的洛谷 p4322 最佳團體,就是用$01$分數規劃&樹形揹包來解決的

// luogu-judger-enable-o2

#include #include using std::min;

using std::max;

const int n = 3e3 + 10, inf = 1e9 + 7;

const double eps = 1e-5;

int n, k, s[n], p[n], son[n][n], dfn[n], time, nx[n];

int from[n], to[n], nxt[n], cnt;//edges

double f[n][n], d[n];

inline void addedge (int u, int v)

inline void upt(double &a, double b)

void dfs (int u)

inline bool check (double k)

return f[n + 1][k] >= eps;

}int main ()

dfs(0);

double l = 0, r = 10000, ans;

while (r - l >= eps)

printf ("%.3lf\n", ans);

return 0;

}

樹形依賴揹包

問題大意 給出一棵樹,根節點為1,每個點有毒素和收穫。要求毒素不超過給定值的情況下使收穫最大。乙個點的父親節點被選取後這個點才能被選取。首先弄出dfs序,也記錄下每個點其子樹及自身的大小。每個點都能夠被選或不選,如果選了才會考慮它子樹。設f i j 表示dfs序上第i位上的點在其子樹及自身上選取了毒...

樹形揹包總結

目錄 2 有物品大小 3 物品大小為1,有k的限制。二 dfs序上dp 例題1例題2 例題3總結下 樹形揹包,就是說,在樹上選乙個包含根的連通塊,或揹包存在依賴關係 選父才能選子 或者需要知道每個點的子樹中選了多少 通常,我們有兩種方法 我們設 dp i,j 表示在i的子節點中選j個的狀態。在轉移時...

樹形揹包DP

include using namespace std const int n 310,m n 2 int h n ne m v m idx int w n int dp n n int n,m void add int a,int b void dfs int u for int j m j 0 ...