樹行DP小結

2022-05-01 04:30:12 字數 2002 閱讀 5727

顧名思義:就是在樹上做的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

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

);}

這道題同樣是樹上跑dp.

狀態很好想,f[x][j]表示x的節點保留j條樹枝的最大值.

#include#define _ 0

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

); }

這裡主要講j和k的範圍,想說j,q-deep[x]+1表示要想選到x這個點必須保留deep[x]+1個樹枝.size[x]-1表示x此時最多選的樹枝.

同理,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項的最值,計算的時候只需要多比較一項,即和前一項比較一下就可以...