Luogu-P2015 二叉蘋果樹
阿新 • • 發佈:2018-11-17
題面傳送門: Luogu-P2015
題目描述
給定一棵 \(n\) 個節點的以 \(1\) 為根的二叉樹(嚴格二叉), 樹邊有邊權. 現在需要剪去一些樹邊(剪邊定義為: 若剪去一條邊 \((u,v)\), 在刪除該邊的同時也必須捨棄以 \(v\) 為根的整個子樹), 問在留下 \(m\) 條邊時邊權之和的最大值.
\(n \le 100,m<n\).
題目分析
非常明顯的樹形DP.
設 \(F[u][j]\) 表示以 \(u\) 為根的子樹中留下 \(j\) 條邊的最大權值, 答案即為 \(F[1][m]\).
1.狀態轉移方程:
\[ F[u][j]=max\{F[u][j-k-1]+F[v][k]+w_{u,v}\}\\ 1 \le j \le min(m,size[u]),0 \le k \le min(j-1,size[v]) \]
其中 \(v\) 是 \(u\) 的一個子節點, \(w_{u,v}\) 表示邊 \((u,v)\) 的權值, \(size[i]\) 表以 \(i\) 為根的子樹中邊的數量, \(k\) 是枚舉出來的, 表示在以 \(v\) 為根的子樹中選 \(k\) 條邊.
2.解釋:
- 若在子樹 \(v\) 中選中了 \(k\) 條邊, 那麼在其他子樹中只能選擇 \(k-j-1\) 條邊(因為當前這條邊 \((u,v)\) 必須選擇, 不然不合法)
- \(k\) 的範圍必須小於等於 \(j-1\) , 因為當 \(k=j\) 時, \(j-k-1\) 的值為 \(-1\), 顯然不合法.
3.複雜度:
因每個點只會訪問一次, 每次訪問列舉需要 \(n^2\) 的複雜度, 故總複雜度為 \(O(n^3)\), 並且常數極小.
實現細節
- 記憶化搜尋.
- \(j,k\) 需要倒序列舉, 原因同 \(01\) 揹包.
- 記憶化搜尋的同時處理 \(size\) 陣列.
程式碼
/********************************************************** * Author : EndSaH * Email : [email protected] * Created Time : 2018-11-17 09:36 * FileName : temp.cpp * *******************************************************/ #include <cstdio> #include <cctype> #include <iostream> namespace Fast_IO {/*{{{*/ char ibuf[1 << 20], obuf[1 << 20], stk[20]; char *ipos = ibuf, *iend = ibuf, *opos = obuf, *oend = obuf + (1 << 20), *stkpos = stk; inline char Getchar() { return ipos == iend and (iend = (ipos = ibuf) + fread(ibuf, 1, 1 << 20, stdin), ipos == iend) ? EOF : *ipos++; } inline void Putchar(char c) { if(opos == oend) { fwrite(obuf, 1, 1 << 20, stdout); opos = obuf; } *opos++ = c; } inline int read() { register int num = 0; register bool flag = false; register char c; while(!isdigit(c = Getchar())) flag |= c == '-'; while(num = (num << 3) + (num << 1) + (c ^ 48), isdigit(c = Getchar())); return flag ? -num : num; } inline void write(int x) { if(x < 0) Putchar('-'), x = -x; do { *stkpos++ = x % 10 ^ 48; x /= 10; } while(x); while(stkpos-- != stk) Putchar(*stkpos); ++stkpos; } }/*}}}*/ using namespace Fast_IO; const int maxN = 102; int n, q, x, y, z, cnt; int head[maxN], F[maxN][maxN], size[maxN]; bool vis[maxN]; struct Chain { int v, w, next; } chain[maxN << 1]; template<typename _Tp> inline bool chkmin(_Tp& x, const _Tp& y) {/*{{{*/ return x > y ? (x = y, true) : false; }/*}}}*/ template<typename _Tp> inline bool chkmax(_Tp& x, const _Tp& y) {/*{{{*/ return x < y ? (x = y, true) : false; }/*}}}*/ inline void Link(int u, int v, int w) { chain[++cnt] = (Chain){v, w, head[u]}; head[u] = cnt; } void DFS(int u) { vis[u] = true; for(register int i = head[u]; i; i = chain[i].next) { int v = chain[i].v; if(vis[v]) continue; DFS(v); size[u] += size[v] + 1; for(register int j = std::min(q, size[u]); j; --j) for(register int k = std::min(j - 1, size[v]); ~k; --k) chkmax(F[u][j], F[u][j - k - 1] + F[v][k] + chain[i].w); } } int main() { #ifndef ONLINE_JUDGE freopen("temp.in", "r", stdin); freopen("temp.out", "w", stdout); #endif n = read(), q = read(); for(register int i = 1; i < n; ++i) { x = read(), y = read(), z = read(); Link(x, y, z); Link(y, x, z); } DFS(1); write(F[1][q]); fwrite(obuf, 1, opos - obuf, stdout); return 0; }