动态规划(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了,准备恶补数学知识三天.

©️2020 CSDN 皮肤主题: 书香水墨 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值