[CSP-S模拟测试]:虎(DFS+贪心)

本文解析了一道算法竞赛题目,通过贪心策略和并查集思想,设计了一个时间复杂度为O(n)的解决方案,详细阐述了如何处理多条边连接一个点的情况,以实现将所有边变为黑色的最少操作次数。

题目传送门(内部题15)


输入格式

第一行一个整数$n$,代表点数
接下来$n-1$行,每行三个数$x,y,z$,代表点$i$与$x$之间有一条边,若$y$为$0$代表初始为白色,否则为黑色,若$z$为$0$代表不对最终颜色做要求,否则代表要求为黑色。


输出格式

达到目的的最少操作多少次数。


样例

样例输入:

7
1 0 1
1 1 1
2 0 1
2 0 1
3 1 1
3 0 1

样例输出:

3


数据范围与提示

对于$30\%$的数据,所有的$x$等于$1$。
对于$70\%$的数据,所有边最终都必须为黑色
对于$100\%$的数据,$n\leqslant 1,000,000$。


题解

先看数据范围,$n\leqslant 1,000,000$(注意是一百万,不是十万,可能只有我数不清几个$0$了吧?),这只能允许我们$\Theta(n)$。

$70\%$算法:

还是先从部分分下手,先来考虑$70\%$的数据,所有便最终都必须为黑色,考虑贪心。

比方说有下面这样一条链:

我们可以选择翻转$1\sim 3$和$4\sim 5$,也可以选择先翻转$1\sim 5$再将$3\sim 4$翻转回来,但是都需要两步,所以我们可以贪心的扫每一条链,直到扫到一条黑边为止,把这中间的都翻转即可。

那么现在来考虑许多边连向一个点的情况:

比方说上面这张图,一共有三个白边连向点$1$,你可能首先会下意识的以为需要翻转三次(聪明的你也可能没有),但是仔细一想,我们可以把这其中任意两个翻转合并,如翻转$2\sim 1\sim 4$这条路径,然后再翻转$1\sim 7$这条路径以达到目的。

那么不妨这样讲,对于多条边连向一个点的情况,其所需的翻转次数即为$\left \lceil \frac{黑边个数}{2} \right \rceil$。

时间复杂度:$\Theta(n)$。

期望得分:$70$分。

实际得分:$60$分。

$100\%$算法:

显然对于一道$T1$来说,我们应该$A$掉它。

发现每条边只会被要求为黑色,或者是任意颜色,所以在来贪心。

对于任意颜色,我们可以无视它,这不太好想,但仔细一想也是对的,我也不知道该怎么解释了,自己体会吧?所以我们可以把这种边缩掉,我的方法是用一个类似并查集思想的东西,但是比并查集简单的多。

时间复杂度:$\Theta(n)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
struct rec{int nxt,to,w;}e[2000001];
int head[1000001],cnt=1;
int n;
bool vis[2000001];
int fa[1000001];
int ans;
void add(int x,int y,int w)
{
	e[++cnt].nxt=head[x];
	e[cnt].to=y;
	e[cnt].w=w;
	head[x]=cnt;
}
void dfs(int x)
{
	for(int i=head[x];i;i=e[i].nxt)
		if(!vis[i]&&!e[i].w)
		{
			vis[i]=vis[i^1]=1;
			dfs(e[i].to);
			return;
		}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=2;i<=n;i++)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		if(!z)fa[i]=fa[x];
		else
		{
			add(i,fa[x],y);
			add(fa[x],i,y);
		}
	}
	for(int x=1;x<=n;x++)
	{
		int sum=0;
		for(int i=head[x];i;i=e[i].nxt)
			if(!vis[i]&&!e[i].w)
			{
				vis[i]=vis[i^1]=1;
				dfs(e[i].to);
				sum++;
			}
		if(sum&1)ans+=sum/2+1;
		else ans+=sum/2;
	}
	printf("%d",ans);
	return 0;
}

rp++

转载于:https://www.cnblogs.com/wzc521/p/11425001.html

09-22
### CSP-S 考试概述 CSP-S(Certified Software Professional – Senior Level)是由中国计算机学会(CCF)主办的非专业级软件能力认证中的提高级考试,主要面向具备一定编程基础的学生,考察其算法设计程序实现能力[^3]。该考试是信息学奥赛(NOI 系列活动)的重要组成部分,成绩优异者可晋级 NOIP 及后续赛事。 ### 编码器相关知识点在 CSP-S 中的应用 在算法竞赛语境下,“编码器”并非指硬件设备,而是泛指数据编码、状态压缩、哈夫曼编码、Base64 编码等信息表示转换相关的技术或思想。这类概念常出现在字符串处理、动态规划、贪心算法等问题中。 例如,在图论问题中使用位运算进行状态压缩时,需将某种组合状态“编码”为整数以便于 DP 数组索引: ```cpp // 使用二进制位表示集合元素的存在性(状态编码) int state = 0; state |= (1 << 3); // 将第3个元素加入集合 if (state & (1 << 3)) { // 判断第3个元素是否存在 } ``` 此类技巧广泛应用于状压DP题目,属于高级选手必备技能之一[^1]。 另外,哈夫曼编码作为经典的数据压缩方法,虽较少直接考查代码实现,但在阅读理解类选择题中可能出现于初赛理论部分,用于测试贪心策略的理解[^3]。 ### 备考指南:核心知识模块梳理 #### 数据结构算法基础 - 基础语法掌握(C++ 推荐为主流语言) - 数组、链表、栈、队列、优先队列(堆) - 字符串操作(KMP、Trie树初步) #### 高阶算法主题 - 动态规划(线性DP、区间DP、背包模型、状压DP) - 图论基本算法DFS/BFS、拓扑排序、最短路Dijkstra/Floyd、最小生成树Prim/Kruskal) - 数论初步(最大公约数、快速幂、模逆元、素数判定) - 树形结构(二叉树遍历、并查集、LCA) 以上内容可通过历年真题训练巩固,推荐利用 `CSP-J/S 2019–2021试题资源库` 进行系统刷题,每道题应配合详细题解深入分析思路演变过程[^4]。 #### 初赛备考建议 初赛侧重理论知识考核,常见考点包括: - 计算机组成原理基础知识 - 时间复杂度分析(O(n log n) vs O() 的实际影响) - 完全二叉树节点关系推导 - 后缀表达式的求值流程 - 逻辑电路布尔代数简化 参考资料中提到的 “CSP-J、CSP-S初赛知识点(2020.09.20).rar” 文件提供了系统的讲义支持,适合集中突破薄弱环节[^2]。 #### 复赛备赛建议 注重调试能力和边界情况处理,编写鲁棒性强的代码至关重要。每次提交前执行如下检查清单: - 输入输出格式严格匹配样例 - 所有变量初始化完成 - 边界条件覆盖完全(n=0, n=1等情况) - 循环终止条件无死循环风险 ### 实战资源获取方式 GitHub 上存在多个高质量开源项目整理了 CSP-S 相关资料,其中包含完整的 PDF 文档合集和可运行代码实例。典型代表即为提供 RAR 压缩包的知识点汇总项目,便于离线查阅打印复习[^7]。 此外,洛谷平台设有专门的 CSP-J/S 模拟考场功能,集成自动评测机制,能够真实还原比赛环境压力体验[^8]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值