顧名思義:就是在樹上做的dp,依據dfs的性質,在訪問過兒子之後返回後將兒子的狀態傳遞給父親...
先看例題:
此題用貪心也能過,不過正解是dp。
對於樹上的dp我們可以直接考慮最優解下各點的狀態來方便我們設狀態.顯然訊號聯通的樹上各點只有三中狀態,自己有塔,兒子有塔,父親有塔.
那我們設狀態時就可以用f[x][0],f[x][1],f[x][2]表示兒子有塔,自己有塔,父親有塔...
對於1和2的狀態比較好轉移:
f[x][1]+=min(f[y][1],min(f[y][0],f[y][2]));
f[x][2]+=min(f[y][1],f[y][0]);
那對於0的狀態,則可以列舉哪個兒子有塔,用計算好的f[x][2]的值:
f[x][0]=min(f[x][0],f[x][2]-min(f[y][1],f[y][0])+f[y][1]); (好好考慮)
初始化,f[x][1]=1;f[x][0]=int_max;
**:
#include#define _ 0這道題同樣是樹上跑dp.using
namespace
std;
const
int maxn=10010
;int n,tot,link[maxn],f[maxn][4],fa[maxn]; //
f[i][1]表示自己用。
struct bian //
f[i][0]表示兒子用.f[i][2]表示父親用.
;bian a[
2*maxn];
inline
void add(int x,int
y)inline
void dfs(int
x)
for(int i=link[x];i;i=a[i].next)
} int
main()
dfs(1);
cout
<1][1],f[1][0
]);
return (0^_^0
);}
狀態很好想,f[x][j]表示x的節點保留j條樹枝的最大值.
#include#define _ 0這裡主要講j和k的範圍,想說j,q-deep[x]+1表示要想選到x這個點必須保留deep[x]+1個樹枝.size[x]-1表示x此時最多選的樹枝.using
namespace
std;
const
int maxn=110
;int
n,q,tot,link[maxn],f[maxn][maxn],size[maxn],fa[maxn],deep[maxn];
struct bian //
f[i][j]表示i點保留了j條邊的最大蘋果數.
;bian a[
2*maxn];
inline
void add(int x,int y,int
v)inline
void dfs(intx)}
intmain()
deep[
1]=1; //
對深度初始化.
dfs(1
); cout
<1][q]<
return (0^_^0
); }
同理,k還多了個j-1,因為還要選x到y這條邊,所以要建議.
這裡警告我:狀態轉移必須在合理的範圍內,否則會出現不可預計的後果.還有f迴圈的順序考慮清楚.
例如此題j就必須是倒序的。因為是拿y來更新x的,比如假如正序:拿f[y][1]更新過f[x][2]後,又拿f[x][2]更新f[x][3]這就不符合情況.此時倒序,由大的列舉就不會出現這種情況了。
好了,就到這了.
樹形dp小結
這些天做了一些樹形dp的題目,感覺有了些領悟,尤其是理解到樹形揹包就是分組揹包之後。選出幾道不錯的總結一下 hdu 1520 hdu 4003 poj 1155 poj 2486 hdu 4313 hdu 4340 hdu 1520 入門水題 每個節點有權值,子節點和父節點不能同時選,問最後能選的最...
區間dp小結
區間dp,顧名思義,就是在區間上dp,即把整個區間劃分為乙個個的小區間,在小區間內dp求出最優值,然後把這些小區間合併以後就是整個取件的最優值。下面是一些比較經典的區間dp題目 1.nyoj 737 石子合併 題意 有n堆石子,每堆有a i 個,每次合併時只能合併相鄰的兩堆,代價為兩堆石子的個數之和...
dp基礎小結
kuangbin帶你飛系列,基礎dp 總共20多道題,就不一一說了 說一下學會的關鍵的思路 第1點 有的時候某一狀態的值的得出,可能會要我們列舉已經計算過的值,一一比較取最值,但如果真的去列舉的話就會超時,這時我們可以把狀態的含義設為前i項的最值,計算的時候只需要多比較一項,即和前一項比較一下就可以...