洛谷 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 是叶子节点,由于叶子节点无法作为非叶子节点的子树,因此可在叶子节点处自由选择建女仆咖啡厅或桌游吧。显然,为了最大化女仆咖啡厅,选择建女仆咖啡厅,此时 dpi=1。
(2)非叶子节点处理‌: 在非叶子节点的子树中,女仆咖啡厅的数目等于桌游吧的数目。这意味着++对于非叶子节点,其子树中的女仆咖啡厅与桌游吧必须成对出现++ 。

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

● 状态表示
dpi:表示以 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

相关推荐
8Qi817 小时前
LeetCode 516:最长回文子序列
算法·leetcode·职场和发展·动态规划
wabs6661 天前
关于动态规划【力扣63.不同路径II与62.不同路径的区别(C++)】自我总结
动态规划
三千里1 天前
路径规划算法-备忘
算法·自动驾驶·动态规划
2601_961845151 天前
新高考一卷真题2025|真题PDF全科整理
线性代数·矩阵·pdf·动态规划·概率论·高考
随意起个昵称1 天前
线性dp-LIS题目5(导弹拦截,二分优化)
c++·算法·动态规划
8Qi81 天前
LeetCode 5:最长回文子串(Longest Palindromic Substring)—— 题解
算法·leetcode·职场和发展·动态规划
8Qi82 天前
LeetCode 1143 & 718:最长公共子序列 / 最长重复子数组
算法·leetcode·职场和发展·动态规划
随意起个昵称2 天前
线性dp-综合刷题1(Not Alone)
算法·动态规划
-森屿安年-2 天前
1137. 第 N 个泰波那契数
c++·动态规划
8Qi82 天前
LeetCode 115 & 392:不同子序列 / 判断子序列
算法·leetcode·职场和发展·动态规划