在算法竞赛的征途中,牛客小白月赛是许多编程爱好者检验基础、提升思维能力的绝佳平台。第127届比赛中的A、B、C、D四道题目,虽然难度定位在入门级,却巧妙地串联起了数组操作、字符串处理、数学思维和贪心策略等多个核心知识点。本文将对这四道题目进行深度解析,不仅还原解题思路,更会提炼出其中蕴含的、可广泛应用于JavaScript、Java、Go、C++等主流语言的通用编程技巧与算法思想。
一、基础遍历与极值查找:寻找“花海”中的王者
第一题A.Flower_Rainbow_and_You是一道典型的热身题。题目要求输入七种颜色花朵的数量,找出数量最多的那种颜色并输出其名称。这本质上是一个在固定长度数组中寻找最大值及其索引的问题。
解题的核心思路非常直接:
- 初始化:设定一个变量记录当前最大值,一个变量记录最大值对应的索引。
- 遍历比较:依次读入七个整数,与当前最大值比较。若新的数值更大,则更新最大值和索引。
- 映射输出:根据最终得到的索引,从一个预定义的颜色名称列表(如
['Red', 'Orange', 'Yellow', 'Green', 'Blue', 'Indigo', 'Violet'])中取出对应的字符串输出。
这道题的价值在于巩固了数组遍历、条件判断和索引映射的基础操作。无论是在Python中直接使用max函数和index方法,还是在C++/Java中手动循环实现,其背后的逻辑是相通的。它提醒我们,在解决更复杂的问题前,扎实的基本功至关重要。
二、同余分组与配对策略:构建“相似二元组”
第二题B.Flower_Rainbow_and_Rhythm将问题提升了一个维度,引入了同余(Modulo)的概念。题目定义:若两个数对k取模的余数相同,则它们可以组成一个“相似度为k”的二元组。给定一个长度为n(偶数)的数组,需要判断能否将所有元素恰好两两配对,使得每对都是相似二元组。
解题的关键在于统计与配对:
- 核心观察:所有元素根据其对k取模的结果,可以被分到k个“余数桶”中。
- ⚙️ 必要条件:要能完成两两配对,每个“余数桶”里的元素数量必须是偶数。因为只有同余的数才能配对,且每个数只能用一次。
因此,算法步骤非常清晰:
- 创建一个大小为k的数组
cnt,用于统计每个余数出现的次数。 - 遍历输入数组,对每个元素
a[i]计算a[i] % k,并增加对应余数桶的计数。 - 遍历
cnt数组,检查是否每个桶的计数都是偶数。若是,则输出"YES",否则输出"NO"。
这道题完美诠释了如何将“配对”问题转化为“计数”问题,是许多涉及匹配、分组类题目的常见思路。在TypeScript或Go中处理大数组时,需注意整型范围和计算效率。
s = ["Red", "Orange", "Yellow", "Green", "Blue", "Indigo", "Violet"]
aList=[int(x) for x in input().split()]
num=0
ans=0
for i in range(7):
if num
三、构造与模式识别:拼接“波形数组”
第三题C.Flower_Rainbow_and_Wave是一道有趣的构造题。题目定义了一种“波形数组”:形如[1,2,...,k,...,2,1],先严格递增到k,再严格递减到1。现在需要判断一个给定长度n的数组,能否由若干个这样的波形数组首尾拼接而成,并给出一个构造示例。
解决构造题,通常需要先发现规律:
- 一个大小为k的波形数组,其长度为
2*k - 1。 - 题目要求由“若干个”拼接,意味着总长度n必须是某些
(2*k - 1)的和。
一个巧妙的构造方法是:始终使用最小的波形数组(k=2,长度为3)进行拼接。因为任何大于等于2的整数n,都可以表示为若干个3的和,再加上0、1或2。具体策略如下:
- 若n能被3整除,则全部由
[1,2,1]拼接。 - 若n % 3 == 1,则可以拆成一个长度为4的序列(如
[1,2,2,1],注意这本身也是一个合法的波形数组拼接?这里需要小心验证题目定义)加上若干个3。更稳妥的方法是,先输出一个[1,2,3,2,1](k=3,长度5),再补足剩下的(长度为n-5),而n-5必定是3的倍数。但题目要求“若干个波形数组拼接”,[1,2,3,2,1]本身就是一个波形数组,合法。 - 若n % 3 == 2,则可以拆成一个长度为2的序列?但波形数组最小长度为3。因此,可以输出一个
[1,2,1](长度3),剩下n-3,其模3余2?这不对。实际上,当n=2时无解。对于n=5,可以直接用[1,2,3,2,1]。对于更大的n,可以构造[1,2,1]加上一个长度为m的序列,使得总长为n。更通用的方法是:总是尝试用k=2和k=3的波形数组来填充。可以证明,除了n=1, 2, 4等少数情况,大部分n都有解。本题需要仔细处理边界条件。
这道题锻炼了选手的逻辑推理、分类讨论和构造能力。在Java或C++实现时,需要编写清晰的判断逻辑和循环构造部分。
def demo():
n, k = map(int, input().split())
aList = [int(x) for x in input().split()]
aDict={}
for i in aList:
x=i%k
aDict[x]=aDict.get(x,0)+1
for i in aDict.values():
if i % 2 == 1:
return "No"
return "Yes"
print(demo())
[AFFILIATE_SLOT_2]
四、滑动窗口与条件优化:寻找魔法装饰的最小区间
第四题D.Flower_Rainbow_and_Balloon是本次比赛最具挑战性的一题,综合了字符串处理、滑动窗口(双指针)和贪心优化。题目背景是:有一排气球(‘r‘红:2分,’y‘黄:1分,’w‘白:0分),可以选择一个连续区间。之后可以进行两种操作:1. 反转区间内红黄颜色(可选);2. 将最多m个白色染成红色。目标是使区间总愉悦度至少达到k,且区间长度尽可能短。
这是一个典型的最小满足条件区间问题,滑动窗口是标准解法:
- 问题转化:对于任意一个区间,我们总可以通过颜色反转操作,使得区间内“红色”气球贡献的分数尽可能多(因为红色分高)。具体来说,我们可以计算不反转和反转两种情况下,红色和黄分别的个数,选择得分更高的那种方案。
- 关键点:在选定颜色方案后,区间基础分固定。白色气球可以视为“潜力股”,每个被染成红色可额外增加2分。因此,要使得总得分>=k,需要满足:
基础分 + 2 * min(白色数, m) >= k。
算法步骤(使用双指针维护滑动窗口):
- 初始化左右指针
l=0, r=0,窗口内计数数组统计r, y, w的数量。 - 移动右指针
r扩大窗口,并更新计数。 - 对于当前窗口,计算两种颜色方案的基础分,取最大值
base_score。计算可获得的额外分 extra = 2 * min(w_count, m)。 - 若
base_score + extra >= k,则记录窗口长度,并尝试移动左指针l缩小窗口,以寻找更优解。 - 重复2-4步,直到遍历完整个字符串。
本题的难点在于高效计算滑动窗口内两种颜色方案的基础分。可以在移动指针时,动态维护窗口内红色和黄色的数量,从而在O(1)时间内计算出反转前后的分数。这要求选手对滑动窗口模型有深刻的理解,并能灵活处理复杂的状态条件。
def demo():
n=int(input())
if n in {1,2,4}:
print(-1)
else:
if n%2==1:
aList=[int(x) for x in range(1,n//2+2)]
bList=aList+list(reversed(aList[:-1]))
else:
n-=3
aList = [int(x) for x in range(1, n // 2 + 2)]
bList = [1,2,1]+aList + list(reversed(aList[:-1]))
print(' '.join(map(str, bList)), end=" ")
print()
t=int(input())
while t:
t-=1
demo()
总结与进阶思考
通过对牛客小白月赛127这四道题目的剖析,我们可以看到一条从基础到进阶的清晰路径:A题巩固数组与查找基础;B题引入同余与分组的数学思想;C题锻炼模式识别与构造能力;D题则综合运用滑动窗口与贪心策略解决最优化问题。
这些题目所涉及的技巧——遍历、计数、同余分类、构造、双指针——是算法竞赛中不可或缺的基石,也同样适用于日常的软件开发,例如在JavaScript中处理UI状态、在Go中构建高性能后端服务、或在C++中优化底层算法。建议学习者在理解解法后,尝试用不同的编程语言重新实现,并思考题目可能的变体(例如,如果D题中白色气球可以染成红色或黄色,策略该如何变化?),从而真正将知识内化,提升解决实际问题的能力。
def demo():
n,m,k=map(int,input().split())
s=input()
if 2*n=k:
min_len=min(min_len,i-l+1)
if s[l] == "r":
numr -= 1
elif s[l] == "y":
numy -= 1
else:
numw -= 1
l+=1
else:
break
if min_len>n:
return -1
else:
return min_len
t=int(input())
while t:
t-=1
print(demo())
浙公网安备 33010602011771号