1. 程式人生 > >樹形dp|無根樹轉有根樹|2015年藍橋杯生命之樹

樹形dp|無根樹轉有根樹|2015年藍橋杯生命之樹

兩個 代碼 ima www. https 2015年 藍橋 ack 大小

2015年藍橋杯第十題——生命之樹(無根樹dfs)

技術分享圖片

①暴力解法:枚舉子集(選點) + dfs判斷連通性(題目要求連通)滿足上面兩個條件下找出最大值權值和

技術分享圖片

②dfs無根樹轉有根樹,遞歸找最優

先學習無根樹轉有根樹

參考博客:https://blog.csdn.net/Originum/article/details/82258450
參考博客:https://www.cnblogs.com/yspworld/p/4270876.html

無根樹轉有根樹模板
void dfs(int cur, int father) {
    for (int i = 0; i < tree[cur].size(); i++) {
        int son = tree[cur][i].v;
        if (son != father) {
            dfs(son, cur);
            //
        }
    }
}

這道題思路:從根(任選一個作為根)出發,自根向下遞歸、自下向上回溯,選取最優(dp),每個節點都有兩種選擇(要、不要這個點)
無根樹的每個節點是平等的,選不同的根沒有區別。

代碼:樹上找最優
技術分享圖片
技術分享圖片

3.樹形dp

學習視頻:https://www.bilibili.com/video/av12194537?from=search&seid=7177934246567735469

代碼參考
技術分享圖片
使用dp數組存放 選當前節點和不選當前點的兩種狀態

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int v[100004];
int dp[100004][2];//表示選擇了當前節點和不選擇的最大分數
int vis[100004];
vector<int> node[100004];
void dfs(int t)
{
    dp[t][1] = v[t];
    dp[t][0] = 0;
    vis[t] = 1;
    for (int i = 0; i < node[t].size(); i++)
    {
        if (!vis[node[t][i]])//如果這個節點沒有走過的話
        {
            dfs(node[t][i]);//繼續往下尋找子節點
            dp[t][1] += max(dp[node[t][i]][0], dp[node[t][i]][1]);//+=是表示選當前節點時,當前節點加上當前節點的子節點的最大序列和
        }
        else//這個子節點是不能走的
        {
            dp[t][1] = max(dp[t][1], v[t]);//所以就比較當前序列和與當前節點的分值的大小比較
            dp[t][0] = max(dp[t][0], 0);
        }
    }
}
int main()
{
    int n;
    cin >> n;
    memset(vis, 0, sizeof(vis));
    for (int i = 1; i <= n; i++)
    {
        cin >> v[i];//輸入分數
    }
    int u, v;
    for (int i = 1; i < n; i++)
    {
        cin >> u >> v;
        node[u].push_back(v);
        node[v].push_back(u);
    }
    dfs(1);
    int ans = -99999;
    for (int i = 1; i <= n; i++)
    {
        ans = max(dp[i][1], ans);
        ans = max(dp[i][0], ans);
    }
    cout << ans << endl;
    system("pause");
    return 0;
}

另外一道類似的樹形dp例題:沒有上司的舞會

技術分享圖片

思路

技術分享圖片

代碼

技術分享圖片

樹形dp|無根樹轉有根樹|2015年藍橋杯生命之樹