1. 牛客周赛Round137参赛体验复盘
上周参加的牛客周赛Round137可以说是一场硬仗,作为算法竞赛老鸟,这次比赛让我重新审视了自己的短板。比赛共6道题,前4题属于常规考察,后2题则需要更深入的算法思维。我最终AC了前5题,最后一题在比赛结束前10分钟才想出正确解法,可惜时间不够没能完成代码。这种"差一点"的体验正是周赛的魅力所在——它总能精准戳中你的知识盲区。
从题目分布来看,Round137延续了牛客一贯的出题风格:数据结构与算法并重,既有考验编码基本功的模拟题,也有需要数学思维的组合问题。特别值得注意的是第三题的树形DP变种,很多选手(包括我)都在这道题上花费了过多时间,导致后面时间紧张。这也暴露出我在复杂状态转移方面的薄弱环节。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 题目详解与解题思路
2.1 第一题:字符串重构
题目要求将给定字符串重新排列,使得相邻字符不相同。这是一道典型的贪心问题,但有个隐藏陷阱——当某个字符出现次数超过(n+1)/2时必然无解。我的解法是:
- 统计字符频率
- 使用最大堆维护当前剩余字符
- 每次取堆顶的两个不同字符交替排列
python复制import heapq
from collections import Counter
def reorganizeString(s):
count = Counter(s)
max_heap = [(-cnt, char) for char, cnt in count.items()]
heapq.heapify(max_heap)
res = []
while len(max_heap) >= 2:
cnt1, char1 = heapq.heappop(max_heap)
cnt2, char2 = heapq.heappop(max_heap)
res.extend([char1, char2])
if cnt1 + 1 < 0:
heapq.heappush(max_heap, (cnt1 + 1, char1))
if cnt2 + 1 < 0:
heapq.heappush(max_heap, (cnt2 + 1, char2))
if max_heap:
cnt, char = max_heap[0]
if -cnt > 1:
return ""
res.append(char)
return ''.join(res)
注意点:当堆中只剩一种字符时,需要特判其数量是否超过1。这是很多选手第一次提交时漏掉的边界条件。
2.2 第二题:矩阵最大子矩形
给定01矩阵,求全1子矩形的最大面积。这道题有多种解法,我采用了经典的单调栈优化方法:
- 预处理每列的高度数组
- 对每行应用柱状图中最大矩形算法
- 使用单调栈优化查找过程
python复制def maximalRectangle(matrix):
if not matrix:
return 0
m, n = len(matrix), len(matrix[0])
height = [0] * (n + 1)
max_area = 0
for row in matrix:
for j in range(n):
height[j] = height[j] + 1 if row[j] == '1' else 0
stack = [-1]
for j in range(n + 1):
while height[j] < height[stack[-1]]:
h = height[stack.pop()]
w = j - stack[-1] - 1
max_area = max(max_area, h * w)
stack.append(j)
return max_area
实际比赛中,有选手尝试用二维前缀和+暴力枚举,这在n=200的数据范围下会超时。这提醒我们:即使想到暴力解法,也要先估算时间复杂度。
3. 比赛中的典型失误分析
3.1 第三题:树形DP的变形
题目给出一个二叉树,要求选择若干节点使得选中的节点不相邻,且权值和最大。这是标准的树形DP问题,但增加了额外限制:如果选择某个节点,则必须同时选择其所有子节点。
我的初始状态定义:
- dp[u][0]:不选u节点时的最大值
- dp[u][1]:选u节点时的最大值
转移方程:
python复制dp[u][0] += max(dp[v][0], dp[v][1]) # v是u的子节点
dp[u][1] += val[u] + sum(dp[v][0] for v in children)
但这样处理会漏掉题目中的关键限制条件。正确做法应该是:
- 选择u节点时,必须选择所有子节点
- 不选u节点时,子节点可选可不选
修正后的状态转移:
python复制def dfs(u):
select = val[u]
not_select = 0
for v in tree[u]:
dfs(v)
select += dp[v][1] # 选了父节点必须选子节点
not_select += max(dp[v][0], dp[v][1])
dp[u][0] = not_select
dp[u][1] = select
这个错误让我损失了至少30分钟调试时间。教训是:必须完全理解题意后再开始编码,特别是状态转移的条件限制。
3.2 第五题:组合数学的应用
题目要求计算满足特定条件的排列数,涉及容斥原理和组合数学。我的解题步骤:
- 计算总排列数:n!
- 减去不符合条件的排列数
- 使用容斥处理多重限制条件
具体公式推导:
code复制ans = n!
- ΣC(n,k)*(n-k)!
+ ΣC(n,k1+k2)*(n-k1-k2)!
- ...
实现时需要注意模运算的处理:
python复制MOD = 10**9 + 7
def solve():
n, k = map(int, input().split())
fact = [1] * (n + 1)
for i in range(1, n+1):
fact[i] = fact[i-1] * i % MOD
inv_fact = [1] * (n + 1)
inv_fact[n] = pow(fact[n], MOD-2, MOD)
for i in range(n-1, -1, -1):
inv_fact[i] = inv_fact[i+1] * (i+1) % MOD
def comb(a, b):
return fact[a] * inv_fact[b] % MOD * inv_fact[a-b] % MOD
# 容斥计算过程
# ...
这类题目在牛客周赛中经常出现,建议平时多练习组合数学相关的题目模板。
4. 比赛策略与时间管理
4.1 题目选择顺序
观察Round137的通过率统计:
- A题:89%
- B题:76%
- C题:45%
- D题:32%
- E题:18%
- F题:5%
我的做题顺序是A→B→D→C→E,这个选择事后证明不太合理。更优策略应该是:
- 先快速解决A、B建立信心
- 浏览所有题目难度
- 优先尝试通过人数多的中等题
特别是当卡在某一题超过30分钟时,应该果断切换题目。这次我在C题上花费了50分钟,严重影响了后续题目的发挥。
4.2 调试技巧
比赛中几个实用的调试方法:
- 对拍:生成随机数据对比暴力解与优化解
- 输出中间变量:在关键步骤打印变量值
- 边界测试:手动构造极端用例
例如在矩阵题中,我添加了以下调试代码:
python复制def debug_print(matrix):
print("Current matrix:")
for row in matrix:
print(' '.join(map(str, row)))
print("Height array:", height)
虽然会略微增加编码时间,但能有效避免低级错误。
5. 备赛建议与资源推荐
5.1 针对性训练计划
根据这次比赛暴露的问题,我制定了新的训练重点:
- 树形DP变种(每周3题)
- 组合数学应用(每周2题)
- 单调栈优化(每周1题)
推荐练习平台:
- 牛客题库:按标签筛选相关题目
- LeetCode:Tag分类训练
- Codeforces:Div2/Div3的D题难度
5.2 常用模板整理
比赛时能快速调用的模板可以节省大量时间。我的模板库包括:
- 快速幂与逆元
- 并查集路径压缩
- 线段树区间查询
- Dijkstra最短路径
- KMP字符串匹配
例如数论模板:
python复制MOD = 10**9+7
def qpow(a, b):
res = 1
while b:
if b & 1:
res = res * a % MOD
a = a * a % MOD
b >>= 1
return res
fact = [1]*(n+1)
for i in range(1,n+1):
fact[i] = fact[i-1]*i % MOD
inv_fact = [1]*(n+1)
inv_fact[n] = qpow(fact[n], MOD-2)
for i in range(n-1,-1,-1):
inv_fact[i] = inv_fact[i+1]*(i+1) % MOD
把这些模板背熟并理解其数学原理,比赛中就能快速应用到相关问题中。
6. 后续提升方向
从这次比赛来看,我需要重点加强以下几个方面的能力:
- 复杂DP的状态设计:特别是树形DP和状态压缩DP的变种题目
- 数学推导能力:组合数学、数论相关的公式推导和证明
- 代码调试效率:减少低级错误,提高一次通过率
具体行动计划:
- 每周参加至少2场虚拟比赛
- 赛后补题时写详细的题解博客
- 组建3人刷题小组互相监督
牛客周赛的题目质量确实很高,既能检验基础算法的掌握程度,又能接触到各种变形题目。坚持参加这类比赛,对算法能力的提升有显著帮助。下次Round138,我的目标是AK所有题目!
