樹上的動態規劃

題目:

點擊打開鏈接


樹上的動態規劃:

f(t, m)表示,在以t爲根的一棵樹中,選出包含根節點t的m個連通的結點,能夠獲得的最高的評分,然後我們的答案就是f(1, M)!

一般的思路會是這樣子的,首先我要包含根節點,然後與根節點連通的結點最開始便是根節點的子結點,而所有選擇的結點都要互相連通的話,那麼如果選擇某一棵子樹中的結點的話就勢必也需要選擇這棵子樹的根節點——所以就變成了一個規模小一些的子問題。比如在求解f(t, m)的時候,我先枚舉t的第一個子結點t1中選出的結點數m1,然後枚舉t的第二個子結點t2中選出的結點數m2……一直到t的最後一個子結點tk中選出的結點數mk,這樣就有f(t, m) = max{f(t1, m1) + f(t2, m2) + …… + f(tk, mk)} + v(t),並且需要保證m1+m2+...+mk+1=m。

這裏不是和無限揹包問題很像麼?我可以不用單獨的求解每一個f(t, m)而是針對於每一個t,同時求解它的f(t, 0..M),這樣的話,我就可以把m視作揹包容量,把每個子結點t_child都視作一件單位重量爲1的物品,但是和揹包問題不同的是,這件物品的總價值並不是單位價值乘以總重量,而是重量爲m_child的該物品的價值爲f(t_child, m_child),這樣我就可以像無限揹包問題一樣,用這樣的方法來進行求解!


可以以後序遍歷的方式訪問這棵樹,這樣當計算f(t, 0..M)的時候,我就已經計算出了所有的f(t_child, m_child)的值,如果我將這些值儲存在數組中的話,我就不需要再遞歸計算了!


代碼:

#include <iostream>
#include <cstring>
using namespace std;

typedef struct edge{
    int to;
    int next;
}e;

const int MAXN = 102;

e edge[MAXN*2];

int N, M;
int head[MAXN], value[MAXN];
int NE;

int f[MAXN][MAXN];

void dfs(int p, int u){
    for(int i=head[u];i!=-1;i=edge[i].next)
        if(p != edge[i].to){
            dfs(u, edge[i].to);
            for(int m = M;m>1;m--){
                for(int m_child = 1;m_child<=m-1;m_child++){
                    f[u][m] = max(f[u][m], f[u][m-m_child]+f[edge[i].to][m_child]);
                }
            }
        }
}

int main(){

    cin>>N>>M;
    memset(head, -1, sizeof(head));
    memset(f, 0, sizeof(f));
    for(int i=0;i<N;i++){
        cin>>value[i];
        f[i][1] = value[i];
    }

    NE = 0;
    for(int i=0;i<N-1;i++){
        int u,v;
        cin>>u>>v;
        u--; v--;

        edge[NE].to = v;
        edge[NE].next = head[u];
        head[u] = NE++;

        edge[NE].to = u;
        edge[NE].next = head[v];
        head[v] = NE++;
    }

    dfs(N, 0);
    cout<<f[0][M]<<endl;

    return 0;
}

發佈了63 篇原創文章 · 獲贊 6 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章