鱼香rose'Blog

洛谷 P1352 没有上司的舞会(树形dp)

\(\Huge{洛谷 P1352 没有上司的舞会(树形dp)}\)

题目链接:P1352 没有上司的舞会 - 洛谷

题意

现有\(N\)名职员,编号为\(1∼N\),他们的关系就像一棵以校长为根的树,父节点就是子节点的直接上司。

每个职员有一个快乐指数\(H_i\),现在有一场宴会需要人来参加,但是没有职员愿意与其直接上司一起参加。参加的职员会获得快乐指数,现在希望获得的快乐指数最高。

思路

一道经典的树形dp问题,来自《算法竞赛进阶指南》。

跟据题意,我们可以发现:对于任意节点,都有选与不选两种状态,唯一的限制条件是其父节点。

因此我们可以出\(f[i][j]\),第一维表示以\(i\)作为根绝点时的最优解,第二维\(j\)有两种状态\(0,1\),分别表示职员\(i\)去或不去。

因此状态转移方程为:

  • \(f(i,0) = \sum\max \{f(x,1),f(x,0)\}\)(上司不参加舞会时,下属可以参加,也可以不参加)
  • \(f(i,1) = \sum{f(x,0)} + H_i\)(上司参加舞会时,下属都不会参加)

实现过程中我们可以以节点\(1\)为根节点然后dfs到最底层,然后逐层向上返回并判断。

标程

const int N = 6e3 + 10; 

vector<int> a(N), b[N], f[N];
int res = 0;

void dfs(int x, int y) {
    if(b[x].empty()) {
        f[x][1] = a[x]; res = max(res, f[x][1]);
        return;
    }
    int sum1 = 0, sum2 = 0, sum3 = 0;
    for(auto i : b[x]) {
        if(i == y) continue;
        dfs(i, x);
        //分别计算其子节点选或不选的和
        sum1 += f[i][0]; sum2 += f[i][1];
        sum3 += max(f[i][0], f[i][1]);
    }

    f[x][1] = sum1 + a[x];//当x选的时候,其子节点不能选
    f[x][0] = sum3;//当x不选的时候,其子节点可选可不选
    res = max(f[x][1], f[x][0]);//判断两种情况的最大值
}

void Solved() { 
    int n; cin >> n;

    for(int i = 1; i <= n; i ++ ) cin >> a[i], f[i].resize(2);

    for(int i = 1; i < n; i ++ ) {
        int x, y; cin >> x >> y;
        b[x].push_back(y); b[y].push_back(x);
    }

    dfs(1, 1);

    cout << res << endl;
}
posted @ 2026-01-15 21:53  鱼香_rose  阅读(0)  评论(0)    收藏  举报