1. 程式人生 > >動態規劃:如何求解最大連通節點值

動態規劃:如何求解最大連通節點值

題目

有N個小球,上面有數字,分別對應1……N,每個小球有個價值,第i個小球價值對應Value[i];有N-1個木棍,一個木棍兩端分別連線著一個小球,把N個小球連線起來,並且保證任意兩個小球間都不存在兩條不同的路徑可以互相到達。

現在要把1號小球連通的M個刷上油漆(連通指的是這一些塗漆的小球可以互相到達並且不會經過沒有塗漆的小球),要求使這M個小球的值得和最大。

輸入

每個測試點(輸入檔案)有且僅有一組測試資料。

每組測試資料的第一行為兩個整數N、M,意義如前文所述。

每組測試資料的第二行為N個整數,其中第i個整數Vi表示標號為i的結點的評分

每組測試資料的第3~N+1行,每行分別描述一根木棍,其中第i+1行為兩個整數Ai,Bi,表示第i根木棍連線的兩個小球的編號。

對於100%的資料,滿足N<=10^2,1<=Ai<=N, 1<=Bi<=N, 1<=Vi<=10^3, 1<=M<=N

測試樣例

樣例輸入

10 4
370 328 750 930 604 732 159 167 945 210 
1 2
2 3
1 4
1 5
4 6
4 7
4 8
6 9
5 10

樣例輸出

2977

解法

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(tk, mk),m1…mk可能有的方案數可是非常多。

其實這個問題非常像完全揹包問題。我們將每個t的每個子節點所代表的子樹看作是一件物品,相當於是在k個子樹中選取M個,使獲得的評分最高。則狀態轉移方程可以變成:

f[t, m] = max{ f[t, m - j] + f[child(i), j] },其中i的取值範圍為t所有的子節點個數, 0 =< j <= M 
單獨對於每棵子樹,我們可以取0....M個節點,然後遍歷每棵子樹,求出所有這些
結果中的最大值即為f[t, m].
對任意一個節點t,計算f[t][0……M]
set f[t][0……M]=0;
f[t][1]=v[t];
f[t][0]=0;

for i = 0……節點t的孩子總數
    for m = M……2
        for j=0……m-1 //j為選取的子樹(以child為根節點的子樹)的節點數,j=0表示不選取該child(i)節點
             f[t][m]=max(f[t][m-j]+f[child(i)][j]);

注意:這個問題跟完全揹包問題還是有一些根本區別的。對於完全揹包問題,每一種物品的價值是一定的,取若干件物品所獲得的最大價值滿足加法分配率f[child][j + i] = f[child][j]  + f[child][i] ;但是對於此問題,不滿足加法分配率,即 f[child][3] != f[child][2]  + f[child][1] .很顯然對於以child為根節點的子樹,取一點、二個點、三個點所獲得的最大值是完全不同的,不等於簡單地相加。

完整程式碼
#include<iostream>
#include<vector>
#include<algorithm>
std::vector<std::vector<int> > G(101);
int Value[101];
int f[101][101];
///pre上一個訪問結點;current當前訪問結點;M:current為根的樹種,結點個數
void Visit(int pre, int current, int M)
{
    if (M == 1)
        return;
    for (int i = 0; i < G[current].size(); ++i)
    {
        if (G[current][i] == pre)
            continue;
        Visit(current, G[current][i], M - 1);
    }

    for (int i = 0; i < G[current].size(); ++i)
    {
        if (G[current][i] == pre)
            continue;
        for (int m = M; m >= 2; --m)
        {
            for (int m_child = 1; m_child < m; ++m_child)
            {
                f[current][m] = std::max(f[current][m], f[current][m - m_child] + f[G[current][i]][m_child]);
            }
        }
    }
}
int main()
{
    int N, M;
    std::cin >> N >> M;
    for (int i = 1; i <= N; ++i)
    {
        std::cin >> Value[i];
        f[i][1] = Value[i];
        f[i][0] = 0;
    }


    int Ai, Bi;
    for (int i = 1; i < N; ++i)
    {
        std::cin >> Ai >> Bi;
        G[Ai].push_back(Bi);
        G[Bi].push_back(Ai);
    }
    Visit(1, 1, M);
    std::cout << f[1][M] << std::endl;


}