動態規劃(7)-------樹形DP

昨天本來就能寫的,結果寫了倆個題寫了四五個小時,於是乎拖到今天了。

沒有上司的舞會

這個題就很好寫了,不像狀態壓縮,真的是難理解…大佬博客
f[u][0]表示從以U爲根節點,不選u的方案。
f[u][1]表示從以U爲根節點,選u的方案。
所以f[u][0] = ∑max(f[si][0],f[si][1])i是遍歷下一層的子節點
f[u][0] = ∑f[si][0] i是遍歷下一層的子節點

#include<bits/stdc++.h>
using namespace std;
const int N=6010;
int happy[N];
int f[N][2];
int h[N],e[N],ne[N],idx;
bool st[N];
int n;
vector<int> ve[N];

void dfs(int u)
{
    f[u][0]=0;
    f[u][1]=happy[u];
    for(int i=0;i<ve[u].size();i++)
    {
        int j=ve[u][i];
        dfs(j);
        f[u][0]+=max(f[j][0],f[j][1]);
        f[u][1]+=f[j][0];
    }
}

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>happy[i];
    for(int i=0;i<n-1;i++)
    {
        int a,b;
        cin>>a>>b;
        ve[b].push_back(a);
        st[a]=true;
    }

     int root=1;
     while(st[root]) 
     	root++;   //求取根節點
     dfs(root);
     cout<<max(f[root][0],f[root][1])<<endl;
     return 0;
}

其實更下面喜歡這樣寫…感覺舒服多了…
如果是線型的一串一維數組的話 不能選擇相鄰的數字 要讓選擇的數的和最大

那麼就有遞推方程dp[i] = max(dp[i-1],dp[i-2] + a[i])

max裏面前一個式子代表我不選a[i] 那麼就能從dp[i-1] 轉移過來
後面一個代表我選了這個 那麼就是從dp[i-2] + a[i] 轉移過來

兩者取最大值就是我們需要的答案 那麼這題的話就可以用這個思想

如果我們選擇了這個節點的值的話 我們就只能從這個節點出發的下下一層轉移了 下一層不能選了

如果我們不選這個節點 我們直接從下一層節點轉移就好了 兩者取最大值 附:大佬博客

#include<bits/stdc++.h>

using namespace std;

const int maxn = 6010;

int val[maxn] , in[maxn];

int dp[maxn] = {0};// 記憶化優化 相當於剪枝了

vector<int> g[maxn]; // 記錄樹

int dfs(int root)
{
    int tans = dp[root]; // 如果前面搜索到了就直接輸出就好了
    if(tans!=0) return tans;
    if(g[root].size() == 0) return val[root]; // 遞歸到葉子節點直接返回
    int ans = val[root] , ans1 = 0 ;// ans 代表我選擇了這個節點 ans1 代表我不選擇這個節點
    for(int i = 0 ; i < g[root].size(); i++) // 第一層
    {
            for(int j = 0 ; j < g[g[root][i]].size(); j++) 
            {
                ans += dfs(g[g[root][i]][j]);// 那麼按照剛纔的思路 ans就是從下下一層轉移
            }
            ans1 += dfs(g[root][i]);// 我不選這個就可以直接從這一層轉移
            tans = max(ans1,ans); // 兩者取一下最大值
    }
    return dp[root] = tans; // 記憶化 避免很多的多餘運算
}

int main()
{
    int n;
    cin>>n;
    for(int i = 1; i <= n; i++)
    {
        cin>>val[i];
    }
    int u,v;
    while(cin>>u>>v&&u!=0)
    {
        g[v].push_back(u);
        in[u]++;
    }
    int root , ans = 0;
    for(int i = 1; i <= n; i++)
    {
        if(in[i]==0) root = i; // 找到根節點開始dfs
    }
    ans = dfs(root);
    cout<<ans<<endl;

}

怎麼說呢,這幾天寫DP寫的真的是心累,智商低的人的苦你們是不會理解的…未來三天寫不寫DP了,準備惡補數學知識三天.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章