洛谷 P1922:女仆咖啡厅桌游吧 ← 树形DP

【题目来源】
https://www.luogu.com.cn/problem/P1922

【题目描述】
小 v 所在的世界被规划成了树形结构,每一个节点上都可以建一个女仆咖啡厅或者桌游吧或者什么都不建。在确定点 1 为根节点之后,规划局要求:对于每一个非叶子的节点 i,设它子树(包括自己)中所有的女仆咖啡厅的数量为 cafe_i,桌游吧数目为 table_i,都有 cafe_i=table_i。
妹妹的问题是:这棵树最多能放多少个女仆咖啡厅。

【输入格式】
输入的第一行是,一个正整数 n,表示世界节点数。
第 2 至 n 行,每行两个正整数 u,v,表示 u 与 v 间有一条边。

【输出格式】
只有一行,最多能放的女仆咖啡厅的个数。

【输入样例】
5
1 2
2 3
3 4
2 5

【输出样例】
2

【数据范围】
对于 30% 的数据,保证 n≤20。
对于 100% 的数据,保证 1≤n≤10^5,1≤u,v≤n。

【算法分析】
● 本题是一道基础的"树形DP"问题。

● 理论上,一个节点的叶子节点数量可以是多个。但题目要求++子树中女仆咖啡厅的数量等于桌游吧的数量++ ,这便限制了子树的构建方式。所以,++叶子节点只能独立存在,不能作为非叶子节点的子树的一部分++ 。如果叶子节点作为非叶子节点的子树,无法满足子树中女仆咖啡厅数量等于桌游吧数量(因为叶子节点只能是 1 个,无法拆分)。

● 本题代码如何判定叶子节点?本题代码采用"链式前向星"存树,将树中的每条无向边视为两条有向边进行存储。自然而然,就有了节点入度的陈述。分析可知,在此种设计下,当一个节点的入度为 1 且不是根节点时,此节点就是叶子节点。

● 约束条件‌:
(1)叶子节点处理‌: 如果 i 是叶子节点,由于叶子节点无法作为非叶子节点的子树,因此可在叶子节点处自由选择建女仆咖啡厅或桌游吧。显然,为了最大化女仆咖啡厅,选择建女仆咖啡厅,此时 dp[i]=1。
(2)非叶子节点处理‌: 在非叶子节点的子树中,女仆咖啡厅的数目等于桌游吧的数目。这意味着++对于非叶子节点,其子树中的女仆咖啡厅与桌游吧必须成对出现++ 。

● 对于任意树,在满足约束条件下,++最多能放置的女仆咖啡厅的数量等于树中非叶子节点的数量++ 。这是因为每个非叶子节点必须通过其子树的女仆咖啡厅和桌游吧平衡来满足约束,而叶子节点只能选择女仆咖啡厅或桌游吧,但为了最大化女仆咖啡厅,应选择女仆咖啡厅。

● 状态表示
dp[i]:表示以 i 为根的子树最多能放的女仆咖啡厅的个数。

● 状态计算

【算法代码】

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N=1e5+5;
int e[N<<1],ne[N<<1],h[N],idx;
int ind[N]; //in-degree
int dp[N];

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

void dfs(int u,int fa)  {
    int cnt=1; //cnt of leaf nodes of u's child
    for(int i=h[u]; i!=-1; i=ne[i]) {
        int j=e[i];
        if(j!=fa) {
            dfs(j,u);
            if(ind[j]==1) cnt++;
            else dp[u]+=dp[j];
        }
    }
    dp[u]+=cnt/2;
}

int main() {
    memset(h,-1,sizeof h);
    int n;
    cin>>n;
    for(int i=1; i<n; i++) {
        int a,b;
        cin>>a>>b;
        add(a,b),add(b,a);
        ind[a]++,ind[b]++;
    }

    dfs(1,0);
    cout<<dp[1]<<endl;

    return 0;
}

/*
in:
5
1 2
2 3
3 4
2 5

out:
2
*/

【参考文献】
https://www.luogu.com.cn/problem/solution/P1922
https://developer.aliyun.com/article/1039179
https://mp.weixin.qq.com/s/k-c63sotpWgVvECsV-9SZw
https://www.cnblogs.com/cangT-Tlan/p/8227355.html

相关推荐
Rock_yzh1 小时前
LeetCode算法刷题——56. 合并区间
数据结构·c++·学习·算法·leetcode·职场和发展·动态规划
Rock_yzh2 小时前
LeetCode算法刷题——53. 最大子数组和
java·数据结构·c++·算法·leetcode·职场和发展·动态规划
王老师青少年编程2 小时前
线性DP第12课:线性DP应用案例实践:数字三角形
c++·动态规划·dp·线性dp·csp·信奥赛·数字三角形
汉克老师2 小时前
CCF-NOI2025第一试题目与解析(第二题、序列变换(sequence))
c++·算法·动态规划·noi
憨憨崽&16 小时前
进击大厂:程序员必须修炼的算法“内功”与思维体系
开发语言·数据结构·算法·链表·贪心算法·线性回归·动态规划
大工mike20 小时前
代码随想录算法训练营第三十四天 | 198.打家劫舍 213.打家劫舍II 337.打家劫舍III
数据结构·算法·动态规划
.格子衫.21 小时前
027动态规划之矩阵DP——算法备赛
算法·矩阵·动态规划
hadage2331 天前
--- 算法 分割回文串 回溯 + 动态规划预处理 ---
算法·动态规划
.格子衫.1 天前
026动态规划之跨步DP——算法备赛
算法·动态规划