蓝桥杯作为国内知名的程序设计竞赛,其真题往往考察选手对基础算法的掌握程度和实际编码能力。这道2014年的分糖果题目,表面上看是一个简单的模拟问题,实则暗藏了对循环队列和边界条件处理的考察。
题目核心要求是:n个小朋友围坐一圈,每人初始有若干糖果。每轮操作分为两个阶段:
这道题的关键在于正确处理小朋友之间的环形关系。在代码中,我们使用vector存储每个小朋友的糖果数,通过模运算实现环形访问:
cpp复制int left = (i - 1 + n) % n; // 计算左边小朋友的下标
这种处理方式避免了复杂的边界判断,当i=0时,(0-1+n)%n=n-1,自然跳转到最后一个元素,形成闭环。
算法采用两阶段处理模式,这是保证操作原子性的关键:
特别注意:分配阶段必须先保存再操作,否则会出现"连锁反应"——前面小朋友的分配会影响后面小朋友的初始值。
cpp复制while(true) {
// 检查是否终止
bool allSame = true;
for(int i = 1; i < n; i++) {
if(candies[i] != candies[0]) {
allSame = false;
break;
}
}
if(allSame) break;
// 分配阶段
vector<int> temp = candies;
for(int i = 0; i < n; i++) {
int give = temp[i] / 2;
int left = (i - 1 + n) % n;
candies[left] += give;
candies[i] -= give;
}
// 补发阶段
for(int i = 0; i < n; i++) {
if(candies[i] % 2 != 0) {
candies[i]++;
teacherGiven++;
}
}
}
give = temp[i] / 2 使用整数除法自动向下取整最坏情况下,算法需要运行O(M)轮,其中M是最大糖果数的对数级。每轮操作包括:
除了输入数组外,算法只需要O(n)的临时空间(temp数组),属于原地算法。
| 测试用例 | 预期输出 | 说明 |
|---|---|---|
| [1,1,1] | 0 | 初始已平衡 |
| [2,4,6] | 3 | 典型情况 |
| [1,3,5] | 4 | 多轮补发 |
| [0,0,2] | 2 | 含0值情况 |
当前每次完整遍历检查终止条件,可以优化为:
通过观察可以发现:
基于这些性质,可以推导出更优的算法,但实现复杂度会显著增加。
这类分配问题在实际中有诸多应用场景:
我在实际编码竞赛中遇到过类似的环形分配问题,关键是要处理好两个要点:一是状态的原子性更新,二是环形结构的边界处理。建议初学者可以先用小规模例子手工模拟,确保完全理解每个步骤的影响。