2024年6月GESP真题及题解(C++七级): 黑白翻转

2024年6月GESP真题及题解(C++七级): 黑白翻转

题目描述

小杨有一棵包含 n n n 个节点的树,这棵树上的任意一个节点要么是白色,要么是黑色。小杨认为一棵树是美丽树当且仅当在删除所有白色节点之后,剩余节点仍然组成一棵树。

小杨每次操作可以选择一个白色节点将它的颜色变为黑色,他想知道自己最少要执行多少次操作可以使得这棵树变为美丽树。

输入格式

第一行包含一个正整数 n n n,代表树的节点数。

第二行包含 n n n 个非负整数 a 1 , a 2 , ... , a n a_1,a_2,\ldots,a_n a1,a2,...,an,其中如果 a i = 0 a_i=0 ai=0,则节点 i i i 的颜色为白色,否则为黑色。

之后 n − 1 n-1 n−1 行,每行包含两个正整数 x i , y i x_i,y_i xi,yi,代表存在一条连接节点 x i x_i xi 和 y i y_i yi 的边。

输出格式

输出一个整数,代表最少执行的操作次数。

输入输出样例 1
输入 1
复制代码
5
0 1 0 1 0
1 2
1 3
3 4
3 5
输出 1
复制代码
2
说明/提示
样例解释

将节点 1 1 1 和 3 3 3 变为黑色即可使这棵树变为美丽树,此时删除白色节点 5 5 5,剩余黑色节点仍然组成一棵树。

数据范围
子任务编号 数据点占比 n n n a i a_i ai 特殊条件
1 1 1 30 % 30\% 30% ≤ 10 5 \leq 10^5 ≤105 0 ≤ a i ≤ 1 0\leq a_i\leq 1 0≤ai≤1 树的形态为一条链
2 2 2 30 % 30\% 30% ≤ 10 5 \leq 10^5 ≤105 0 ≤ a i ≤ 1 0\leq a_i\leq 1 0≤ai≤1 只有两个节点颜色为黑色
3 3 3 40 % 40\% 40% ≤ 10 5 \leq 10^5 ≤105 0 ≤ a i ≤ 1 0\leq a_i\leq 1 0≤ai≤1

对于全部数据,保证有 1 ≤ n ≤ 10 5 1\leq n\leq 10^5 1≤n≤105, 0 ≤ a i ≤ 1 0\leq a_i\leq 1 0≤ai≤1。

思路分析

算法思路
  1. 问题转化:美丽树要求删除所有白色节点后,剩余黑色节点仍构成一棵树,即所有黑色节点必须连通。通过将白色节点变为黑色来连接黑色节点,需要找到最少的白色节点数,使得所有黑色节点连通。
  2. 核心观察:从任意一个黑色节点(如第一个黑色节点)作为根进行DFS,计算每个节点的子树中黑色节点的数量。若一个节点的子树中包含黑色节点,则该节点必须保留(如果是白色则需要变为黑色),否则删除该节点不会影响黑色节点的连通性。
  3. 计算最少操作:统计所有子树中包含黑色节点的节点数,减去初始黑色节点数,即为需要将白色变为黑色的节点数(最少操作次数)。
代码流程
  • 初始化:读入节点颜色,记录黑色节点数并选择第一个黑色节点作为根。
  • 建图:读入边,构建无向树。
  • DFS计算 :从根节点开始DFS,后序遍历累加子树中黑色节点数到父节点,使vis[i]最终表示以i为根的子树中黑色节点的总数。
  • 统计结果 :遍历所有节点,若vis[i] > 0则说明该节点的子树中有黑色节点,计数一次。最终ans为所有此类节点数,减去初始黑色节点数num1,得到需要操作的白色节点数。
时间复杂度
  • DFS遍历所有节点和边,时间复杂度为O(n)。
  • 空间复杂度为O(n),用于存储树和图。

代码实现

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

const int N = 200010;  // 定义最大节点数,开两倍以防无向边存储
int n, a, b, ans, num, root; // n:节点数,a,b:临时边变量,ans:统计结果,num1:黑色节点数量,root:选定的根节点(第一个黑色节点)
int vis[N];           // vis[i]:初始表示节点i的颜色(黑色为1,白色为0),DFS后表示以i为根的子树中黑色节点的总数
vector<int> e[N];     // 邻接表存储树的边

// 深度优先搜索,计算每个节点的子树中黑色节点数量
// u: 当前节点,f: 父节点
void dfs(int u, int f) {
    // 遍历当前节点的所有邻居
    for (int v : e[u]) {
        if (v == f) continue; // 跳过父节点,避免回环
        dfs(v, u);            // 递归处理子节点
        vis[u] += vis[v];     // 将子节点的黑色节点数累加到当前节点
    }
    return;
}

int main() {
    // 读入节点数
    scanf("%d", &n);
    int c;
    // 读入每个节点的颜色,并初始化vis数组
    for (int i = 1; i <= n; i++) {
        scanf("%d", &c);
        if (c == 1) {           // 如果节点是黑色
            vis[i] = 1;         // 标记该节点为黑色(计数1)
            num++;             // 黑色节点计数加一
            if (num == 1) root = i; // 记录第一个黑色节点作为根节点
        }
        // 白色节点vis[i]保持为0
    }

    // 读入n-1条边,构建树的无向图
    for (int i = 1; i < n; i++) {
        scanf("%d%d", &a, &b);
        e[a].push_back(b);
        e[b].push_back(a);
    }

    // 从根节点(第一个黑色节点)开始DFS,计算每个节点的子树中黑色节点数量
    dfs(root, 0);

    // 统计所有子树中包含黑色节点的节点数(即vis[i] > 0的节点)
    for (int i = 1; i <= n; i++) 
        ans += bool(vis[i]); // 如果vis[i] > 0则加1,否则加0

    // 输出最少操作次数:需要变黑的白色节点数 = 包含黑色节点的节点数 - 初始黑色节点数
    printf("%d\n", ans - num);
    return 0;
}

各种学习资料,助力大家一站式学习和提升!!!

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	cout<<"##########  一站式掌握信奥赛知识!  ##########";
	cout<<"#############  冲刺信奥赛拿奖!  #############";
	cout<<"######  课程购买后永久学习,不受限制!   ######";
	return 0;
}

1、csp信奥赛高频考点知识详解及案例实践:

CSP信奥赛C++动态规划:
https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转

CSP信奥赛C++标准模板库STL:
https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转

信奥赛C++提高组csp-s知识详解及案例实践:
https://blog.csdn.net/weixin_66461496/category_13113932.html

2、csp信奥赛冲刺一等奖有效刷题题解:

CSP信奥赛C++初赛及复赛高频考点真题解析(持续更新):https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转

CSP信奥赛C++一等奖通关刷题题单及题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12673810.html 点击跳转

3、GESP C++考级真题题解:

GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转

GESP(C++ 七级+八级)真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13117178.html

4、CSP信奥赛C++竞赛拿奖视频课:

https://edu.csdn.net/course/detail/40437 点击跳转

· 文末祝福 ·

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	cout<<"跟着王老师一起学习信奥赛C++";
	cout<<"    成就更好的自己!       ";
	cout<<"  csp信奥赛一等奖属于你!   ";
	return 0;
}
相关推荐
ouliten2 小时前
C++笔记:std::span
c++·笔记
小尧嵌入式2 小时前
【Linux开发二】数字反转|除数累加|差分数组|vector插入和访问|小数四舍五入及向上取整|矩阵逆置|基础文件IO|深入文件IO
linux·服务器·开发语言·c++·线性代数·算法·矩阵
一只小bit2 小时前
Qt 网络:包含Udp、Tcp、Http三种协议的客户端实践手册
前端·c++·qt·页面
jojo_zjx2 小时前
GESP 23年6月2级 找素数
c++
kimicsdn2 小时前
opentelemetry-demo currency cpp 项目编译流程分享
c++·cmake·libprotobuf-dev
闻缺陷则喜何志丹2 小时前
【动态规划】P9980 [USACO23DEC] Flight Routes G|普及+
c++·算法·动态规划·洛谷
Tisfy2 小时前
LeetCode 3314.构造最小位运算数组 I:今日先简单题简单做-到II再优化
算法·leetcode·题解·模拟·数组
橘颂TA2 小时前
【剑斩OFFER】算法的暴力美学——力扣 433 题:最小基因变化
数据结构·c++·算法·哈希算法
移幻漂流2 小时前
C/C++内存掌控之道:从内存泄漏到零开销抽象的进阶之路
java·c语言·c++