自上而下树状DP,max用法是什么

自上而下树状DP,max用法是什么

#include<bits/stdc++.h>
using namespace std;
#define maxn 110000
int n, val[maxn];
struct Edge{
    int nex, to;
}edge[maxn<<1];
int head[maxn], cnt;
int f[maxn][2];
void add(int from, int to){
    edge[++cnt].nex = head[from];
    head[from] = cnt;
    edge[cnt].to = to;
    return;
}
void dfs(int u,int fa){
    for (int i = head[u]; i; i = edge[i].nex){
        int v = edge[i].to;
        if (v != fa)continue;
        dfs(v, u);
        //代表v这个儿子的状态已经被处理了
        f[u][0] += max(f[v][0], f[v][1]);
        //代表u这个点不选,那么v只能进
    }
    return;
}
const int N = 150;
​
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)cin >> val[i], f[i][1] = val[i];
    for (int i = 1; i < n; ++i){
        int u, v;
        cin >> u >> v;
        add(u, v), add(v, u);
    }
    dfs(1, 0);
    cout << max(f[1][0], f[1][1]);
    return 0;
}

#include<bits/stdc++.h>
using namespace std;
#define maxn 110
int n, V;
int f[maxn][maxn];
int w[maxn],v[maxn];
vector<int>g[maxn];
struct Edge{
    int nex, to;
}edge[maxn<<1];
int head[maxn], cnt;
void add(int from, int to){
    edge[++cnt].nex = head[from];
    head[from] = cnt;
    edge[cnt].to = to;
    return;
}
void dfs(int u,int fa){
    memset(f[u], -0x3f, sizeof(f[u]));
    //
    if (v[u] <= V)f[u][v[u]] = w[u];//u这个节点为根
    for (int i = head[u]; i; i = edge[i].nex){
        int v = edge[i].to;
        if (v == fa)continue;
        dfs(v, u);
        vector<int>nf(f[u], f[u] + V + 1);
        for (int v1 = 0; v1 <= V; v1++){
            for (int v2 = 0; v1 + v2 <= V; v2++){
                nf[v1 + v2] = max(nf[v1 + v2], f[u][v1] + f[v][v2]);
            }
        }
        for (int v = 0; v <= V; v++)f[u][v] = nf[v];
    }
    return;
}
int main()
{
    cin >> n >> V;
    for (int i = 1; i < n; i++){
        int u, v;
        add(u, v); add(v, u);
    }
    dfs(1, 0);
    int ans = 0;
    for (int i = 0; i <= V; i++)ans = max(ans,f[1][i]);
    cout << ans << "\n";
    return 0;
}

简单来讲,就是当前节点和儿子结点之间有限制关系,那么为了解决这个问题,我们多开一维记录当前节点选的状态即可。

树上背包的trick很常见,掌握十分必要。

  • 21
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值