題解:
考這場考試的前一天晚上逛大神部落格,看見別人有寫樹上揹包,嗯,一笑而過了。。。。結果第二天xjoi的提高組模擬賽就考了。於是不會,然後今天在上課的時候發呆的時候,自己腦補出來樹上揹包的做法。嗯,看了一下大神的部落格,我發現大神的樹上揹包是用的記憶化搜尋,我最開始的想法是基於拓撲排序從下往上dp,嗯,複雜了,改進了一下我的方法。
一般的樹上揹包:(60分解法)
定義:dp[i][j]為到點i已經裝入j個點所能獲得的最大價值。
然後我們的狀態轉移方程為: dp[i][j] = max(dp[i][j],dp[i][j-k] + dp[son[i]][k]);
要遍歷n個點,顯然j要從1-lim迴圈,k也要從1-lim迴圈。所以時間複雜度為o(n*lim^2)
可以過n <= 100的資料,對於本題60分是夠了的。ps:對於善良的出題人,也都是可以ac的。
優化一下的樹上揹包:(100分解法)
同樣定義:dp[i][j]為到點i已經裝入j個點所能獲得的最大價值,再多定義乙個sz[i]用來表示i的以搜尋子樹大小;
然後我們的狀態轉移方程為: dp[i][j] = max(dp[i][j],dp[i][j-k] + dp[son[i]][k]);
但是我們稍微思考一下我們每一次狀態轉移的時候都不用遍歷所有的狀態,因為我們有效的狀態沒有那麼多個,畫個圖發現我有效的狀態,只有我們前面搜尋的所有樹上所有點,也就是sz[i],然後我可以更新我這個節點的子樹的狀態為sz[son[i]],這樣的話更新我這個狀態的時間複雜度就由原來的lim^2變成了蜜汁時間複雜度,感覺這兒應該涉及到均攤和期望?反正我不太會算,請各位大神解答一下感激不盡
對了別人官方題解給出的**和我的解答是一樣的,但是按照我的理解似乎官方解答和我的解答的思路就不一樣。
官方題解:
有興趣的可以去別人哪兒看,我就不發了。
xjoi題解
**:
#include
#include
const
int maxn = 3005,inf = 1e9;
using
namespace
std;
int n,k,e = 1,ans;
int head[maxn],d[maxn],dp[maxn][maxn],sz[maxn];
bool vis[maxn];
struct nodeedge[10*maxn];
inline
void addedge(int u,int v)
;head[u] = e++;
}inline
void init()
}int dfs(int u,int fa)
}sz[u]++;
for(int i = sz[u];i >= 1;i--)
dp[u][i] = dp[u][i-1] + d[u];
dp[u][0] = -inf;
}int main()
樹上揹包練習
p2014 ctsc1997 選課 p2014 ctsc1997 選課 solution 樹上揹包模板題 因為有多節課是沒有先修課的,所以並不是只有一棵樹,用乙個0號點作為沒有先修課的課程的先修課,這樣就合併成了一棵樹,只要選取m 1個點 必選0 即可。轉移方程 dp u j max dp u j ...
重建道路 樹上揹包
初始化 void init struct edges edge maxm 1 無向圖則需要乘2 inline void add int u,int v head u cnt int dp m m siz m tmp m int n,m void dfs int u,int fa siz u siz ...
樹上依賴揹包總結
做了很多關於樹上dp的題有些使用了nm 2的演算法來進行dp,可是當m過大時方法不大試用,又可以對這種演算法進行優化,如dfs序或者直接樹上合併揹包。當輸出方案時又需要多叉轉二叉進行dp尋找方案,很多方法於是乎總結一下。w i 表示第i個節點的重量,v i 表示第i個節點的價值,f i j 表示以i...