刷题周记(六)——#树形DP:没有上司的舞会、二叉苹果树

——2020年12月 4日(周五)——————————————————

沉思……了这么多天都沉思了些什么?
那就是试着自己做出树形DP的几道题目……
至于结果嘛……
没错我失败了……
不得不承认,没有跨过所谓的那一道 ”坎“ 真的很难啊……浪费了 一周时间了

#树形DP

一、没有上司的舞会

285. 没有上司的舞会
注释版代码

#include<bits/stdc++.h>
using namespace std;
const int N = 6010;
int f[N][2];
int happy[N];
bool b[N];

int h[N], ne[N], e[N], idx;
//上司a,下属b
void add(int a, int b){
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

void dfs(int x){
    f[x][1] = happy[x];
    for(int i = h[x]; i != -1; i = ne[i]){
        int j = e[i];
        //这里是个递归的过程,和我一开始想的不太一样。
        dfs(j);
        //x不来(不选x)
        f[x][0] += max(f[j][0], f[j][1]);
        //x来啦
        f[x][1] += f[j][0];
        
    }
}

int main(){
    memset(h, -1, sizeof h);
    int n; cin >> n;
    for(int i = 1; i <= n; i ++) scanf("%d", &happy[i]);
    
    for(int i = 1; i < n; i ++){
    	//下属,上司
        int l, k; scanf("%d%d", &l, &k);
        //记录节点状态,如果有粑粑了,就不会是根节点了啊
        b[l] = 1;
        //上属,下属
        add(k, l);
    }
    //找到根节点
    int root = 1;
    while(b[root]) root ++;
    //从根节点开始出发,一路搜下去(记忆化搜索)
    dfs(root);
    //最后的结果会在根节点选或不选之间。
    printf("%d", max(f[root][0], f[root][1]));
    
    return 0;
}

纯净版代码

#include<bits/stdc++.h>
using namespace std;
const int N = 6010;

int f[N][2];

int happy[N];
bool b[N];

int h[N], ne[N], e[N], idx;
void add(int a, int b){
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

void dfs(int x){
    f[x][1] = happy[x];
    for(int i = h[x]; i != -1; i = ne[i]){
        int j = e[i];
        dfs(j);
        
        f[x][0] += max(f[j][0], f[j][1]);
        f[x][1] += f[j][0];
        
    }
}

int main(){
    memset(h, -1, sizeof h);
    int n; cin >> n;
    for(int i = 1; i <= n; i ++) scanf("%d", &happy[i]);
    for(int i = 1; i < n; i ++){
        int l, k; scanf("%d%d", &l, &k);
        b[l] = 1;
        add(k, l);
    }
    int root = 1;
    while(b[root]) root ++;
    dfs(root);
    printf("%d", max(f[root][0], f[root][1]));
    
    return 0;
}

——2020年12月 5(26)日(周六)——————————————————

隔了三周了,才有机会再次学习树形DP
(看着三周前的学习记录感觉还行,就是当时很难理解心态有点崩,于是用上了很多奇奇怪怪的语言……请忽略

二、二叉苹果树

DP就是用来记录某个状态下的最优解的。
树形DP不用枚举状态而是搜索状态,本题就是用了深搜的思想进行搜索;
从根节点开始往下走,一步步地记录各个状态下的最优解,最后供目标状态使用。

注释版代码

下面的代码用的是邻接表储存的树,有个好处就是不论是多少叉的树都可以储存,不局限于本题的二叉树。

#include<bits/stdc++.h>
using namespace std;
const int N = 110;

int f[N][N];
int n, q;

int sum[N];
//树的储存,随便用什么只要能够储存就可以了。这里用的是邻接表,个人习惯(因为只会这个QwQ)
int idx, h[N], nxt[2 * N], e[2 * N], w[2 * N];
void add(int a, int b, int c){
    e[idx] = b, w[idx] = c, nxt[idx] = h[a], h[a] = idx ++;
}

void dfs(int now, int dad){
    for(int i = h[now]; i != -1; i = nxt[i]){
        int son = e[i], w_son = w[i];
        if(son == dad) continue;
        dfs(son, now);
        
        sum[now] += (sum[son] + 1);
        //这个子树上留几条边?从大到小排列!要是超过了要留的最大边数就用要留的最大边数,没超过就算了。
        //这样枚举是为了不重复使用一棵子树
        //由于每次枚举子树都是从左往右枚举过来的,那么当前f[now][v_now]中的值都是前面的子树造成的
        //这样倒过来枚举,每一棵子树,它只会被上一轮的值影响到。
        for(int j = min(q, sum[now]); j > 0; j --)
            //给子树留几条边?最多给它留的边只有j - 1条
            for(int k = 0; k <= j - 1; k ++)
                f[now][j] = max(f[now][j], f[now][j - 1 - k] + f[son][k] + w_son);
    }
}

int main(){
    memset(f, 0, sizeof f);
    memset(h, -1, sizeof h);
    cin >> n >> q;
    for(int i = 1; i < n; i ++){
        int u, v, w1; scanf("%d%d%d", &u, &v, &w1);
        add(u, v, w1); add(v, u, w1);
    }
    f[1][0] = 0;
    dfs(1, 0);
    cout << f[1][q];
    return 0;
}

纯净版代码

#include<bits/stdc++.h>
using namespace std;
const int N = 110;

int f[N][N];
int n, q;

int sum[N];
int idx, h[N], nxt[2 * N], e[2 * N], w[2 * N];
void add(int a, int b, int c){
    e[idx] = b, w[idx] = c, nxt[idx] = h[a], h[a] = idx ++;
}

void dfs(int now, int dad){
    for(int i = h[now]; i != -1; i = nxt[i]){
        int son = e[i], w_son = w[i];
        if(son == dad) continue;
        dfs(son, now);
        
        sum[now] += (sum[son] + 1);
        for(int j = min(q, sum[now]); j > 0; j --)
            for(int k = 0; k <= j - 1; k ++)
                f[now][j] = max(f[now][j], f[now][j - 1 - k] + f[son][k] + w_son);
    }
}

int main(){
    memset(f, 0, sizeof f);
    memset(h, -1, sizeof h);
    cin >> n >> q;
    for(int i = 1; i < n; i ++){
        int u, v, w1; scanf("%d%d%d", &u, &v, &w1);
        add(u, v, w1); add(v, u, w1);
    }
    f[1][0] = 0;
    dfs(1, 0);
    cout << f[1][q];
    return 0;
}

后来我又找到了之前做的代码……应该是当时懒得写笔记了。
其实都是差不多的啦。

#include<bits/stdc++.h>
using namespace std;
const int N = 110, M = 220;
int f[N][N];
int e[M], w[M], ne[M], h[M], idx;
int n, m;

void add(int a, int b, int w1){
    e[idx] = b, w[idx] = w1, ne[idx] = h[a], h[a] = idx ++;
}

void dfs(int u, int fa){
    for(int i = h[u]; i != -1; i = ne[i]){
        if(e[i] == fa) continue;
        dfs(e[i], u);
        for(int j = m; j >= 0; j --)
            for(int k = 0; k + 1 <= j; k ++)
                f[u][j] = max(f[u][j], f[e[i]][k] + f[u][j - 1 - k] + w[i]);

    }
}

int main(){
    memset(h, -1, sizeof h); 
    cin >> n >> m;
    for(int i = 1; i < n; i ++){
        int a, b, w1; 
        scanf("%d%d%d", &a, &b, &w1);
        add(a, b, w1); add(b, a, w1);
    }
    dfs(1, -1);
    printf("%d", f[1][m]);
    return 0;
}

——(完)——————————————————

To be continue……

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值