【算法学习笔记】树形DP(没有上司的舞会)

 在acwing上学习算法的一点思考与总结


每一个节点的情况无非就两种:选和不选。那就开设两个记录答案的数组,第一个数组是选择根节点时的总高兴度,第二个是不选根节点时的总高兴度。

具体的代码就要用dfs来实现,因为dfs会先选择一条路往死里搜,搜到最后一个节点就会返回到它上一个节点,然后再去搜索它的分支。这保证了分支与分支之间不受影响。

需要注意的是在这个题中的图是有向图,区别于 树的重心那题是有向图,为了防止无限次向上递归,我们需要设置一个bool数组st[ ]来判断这个节点是否已被走过。

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 6010;

int n;
int h[N], e[N], ne[N], idx; //模拟树
int happy[N]; //录入高兴度
int f[N][2]; //存储选和不选时的高兴度
bool has_fa[N]; //判断是否为父节点

void add(int a, int b)  //建立邻接表
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

void dfs(int u)
{
    f[u][1] = happy[u]; //赋值该点的高兴度

    for (int i = h[u]; ~i; i = ne[i]) //从头节点开始遍历邻接表
    {
        int j = e[i]; //
        dfs(j); //回溯

        f[u][1] += f[j][0];
        f[u][0] += max(f[j][0], f[j][1]);

        //不用写return,当dfs搜索完所有节点时,才会开始计算高兴度,这整个计算过程都是在for循环内执行。当返回到根节点时两种情况的高兴度都计算完,自动退出循环。
    }
}

int main()
{
    scanf("%d", &n);

    for (int i = 1; i <= n; i ++ ) scanf("%d", &happy[i]);

    memset(h, -1, sizeof h);
    for (int i = 0; i < n - 1; i ++ )
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(b, a);
        has_fa[a] = true; //标记为已有父节点
    }

    int root = 1;
    while (has_fa[root]) root ++ ; //寻找根节点

    dfs(root);

    printf("%d\n", max(f[root][0], f[root][1])); //取两种情况最大值输出

    return 0;
}

猜你喜欢

转载自blog.csdn.net/Radein/article/details/134899393