P1352 没有上司的舞会

原题链接:P1352 没有上司的舞会
一道经典的树形dp题。(我看未必)。由于这里是有向边的“树”,是一个典型的DAG(有向无环图),所以可以在拓扑排序中进行状态转移。注意状态转移方程中细微的区别。下面给出2种方法的代码。

状态转移方程的解释:f[][]第一维表示节点的标号,第二维只有2位,0表示选择不选此节点,1表示选择此节点。

拓扑排序:(v为父亲节点,u为儿子节点)
\(f[v][0] = \sum_\limits{(u,v)\in e} max(0, max(f[u][0], f[u][1]))\)
\(f[v][1] = \sum_\limits{(u,v)\in e} max(0, f[u][0])\)

// 拓扑序做法
#include <cstdio>
#include <algorithm>
using namespace std;

const int N = 6005;
int n;
int r[N];
int h[N], e[N], ne[N], idx = 1;
int f[N][2];
// 节点入度,拓扑序的层数,根节点
int deg[N], d[N], root;

void insert(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void topo_sort() {
    // 队列
    static int q[N], hh = 1, tt = 0;
    for (int i = 1; i <= n; ++i) {
        // 初始化dp边界
        f[i][0] = 0, f[i][1] = r[i];
        if (!deg[i]) q[++tt] = i, d[i] = 1;
    }

    while (hh <= tt) {
        int u = q[hh++];

        for (int i = h[u]; i; i = ne[i]) {
            int v = e[i];

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

            deg[v]--;
            if (!deg[v]) q[++tt] = v;

            if (d[v] < d[u] + 1) {
                d[v] = d[u] + 1;
                root = v;
            }
        }
    }
}
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) scanf("%d", &r[i]);
    for (int i = 1; i < n; ++i) {
        int l, k;
        scanf("%d%d", &l, &k);
        // 建立儿子指向父亲的边
        insert(l, k);
        deg[k]++;
    }

    topo_sort();
    
    printf("%d\n", max(f[root][0], f[root][1]));
}

树形dp:(u为父亲节点,v为儿子节点)
\(f[u][0] = max(f[u][0], f[u][0] + max(f[v][0], f[v][1]))\)
\(f[u][1] = max(f[u][1], f[u][1] + f[v][0])\)

// 树形dp做法
#include <cstdio>
#include <algorithm>
using namespace std;

const int N = 6005;
int n;
int r[N], deg[N];
int h[N], e[N], ne[N], idx = 1;
int f[N][2];

void insert(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u) {
    f[u][0] = 0;
    f[u][1] = r[u];

    for (int i = h[u]; i; i = ne[i]) {
        int v = e[i];

        dfs(v);

        f[u][0] = max(f[u][0], f[u][0] + max(f[v][0], f[v][1]));
        f[u][1] = max(f[u][1], f[u][1] + f[v][0]);
    }
}
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) scanf("%d", &r[i]);
    for (int i = 1; i < n; ++i) {
        int l, k;
        scanf("%d%d", &l, &k);
        // 建立父亲指向儿子的边
        insert(k, l);
        deg[l]++;
    }

    int root;
    for (int i = 1; i <= n; ++i) if (!deg[i]) {
        root = i;
        break;
    }

    dfs(root);

    printf("%d\n", max(f[root][0], f[root][1]));
}
posted @ 2021-03-02 21:47  wWwWw233  阅读(126)  评论(0)    收藏  举报