题目描述
现在有一队小朋友,他们高矮不同,我们以正整数数组表示这一队小朋友的身高,如数组{5,3,1,2,3}。
我们现在希望小朋友排队,以“高”“矮”“高”“矮”顺序排列,每一个“高”位置的小朋友要比相邻的位置高或者相等;每一个“矮”位置的小朋友要比相邻的位置矮或者相等;
要求小朋友们移动的距离和最小,第一个从“高”位开始排,输出最小移动距离即可。
例如,在示范小队{5,3,1,2,3}中,{5, 1, 3, 2, 3}是排序结果。
{5, 2, 3, 1, 3} 虽然也满足“高”“矮”“高”“矮”顺序排列,但小朋友们的移动距离大,所以不是最优结果。
移动距离的定义如下所示:
第二位小朋友移到第三位小朋友后面,移动距离为1,若移动到第四位小朋友后面,移动距离为2;
输入描述
排序前的小朋友,以英文空格的正整数:
4 3 5 7 8
注:小朋友<100个
输出描述
排序后的小朋友,以英文空格分割的正整数:4 3 7 5 8
备注:4(高)3(矮)7(高)5(矮)8(高), 输出结果为最小移动距离,只有5和7交换了位置,移动距离都是1。
用例
输入 | 4 1 3 5 2 |
输出 | 4 1 5 2 3 |
说明 | 无 |
输入 | 1 1 1 1 1 |
输出 | 1 1 1 1 1 |
说明 | 相邻位置可以相等 |
输入 | xxx |
输出 | [ ] |
说明 | 出现非法参数情况, 返回空数组。 |
小朋友排队问题:高矮交替排列的最小移动距离
核心解题思路
本题目要求将小朋友按照"高-矮-高-矮"的顺序排列,同时最小化移动距离。移动距离定义为小朋友位置变化的绝对值(如从位置2移动到位置3,移动距离为1)。核心解题思路如下:
关键观察
-
高矮交替规则:
- 偶数位置(0,2,4,…)是"高"位:当前身高 ≥ 下一个身高
- 奇数位置(1,3,5,…)是"矮"位:当前身高 ≤ 下一个身高
- 允许相等:相邻位置身高可以相等
-
最小移动距离策略:
- 只进行相邻元素交换,保证每次移动距离最小(移动距离=1)
- 采用贪心算法:从左到右遍历,不满足条件时立即交换
- 多轮处理:交换后可能影响前面位置,需要重新检查
-
算法流程:
graph TD A[输入身高数组] --> B{检查非法输入} B -->|非法| C[返回空数组] B -->|合法| D[初始化交换标志] D --> E[遍历数组] E --> F{当前位置是否满足条件?} F -->|是| G[继续下一个位置] F -->|否| H[交换相邻元素] H --> I[设置交换标志] I --> J[完成遍历?] J -->|是| K{是否发生交换?} K -->|是| D K -->|否| L[输出结果]
完整代码实现
def adjust_heights(heights):
n = len(heights)
# 创建交换标志,初始为True以进入循环
swapped = True
while swapped:
swapped = False
# 遍历除最后一个位置的所有元素
for i in range(n - 1):
if i % 2 == 0: # 偶数位置:高位
if heights[i] < heights[i + 1]:
# 交换相邻元素
heights[i], heights[i + 1] = heights[i + 1], heights[i]
swapped = True
else: # 奇数位置:矮位
if heights[i] > heights[i + 1]:
# 交换相邻元素
heights[i], heights[i + 1] = heights[i + 1], heights[i]
swapped = True
return heights
def main():
try:
# 读取输入
data = input().split()
# 检查空输入
if not data:
print("")
return
# 转换为整数并验证
heights = []
for s in data:
if not s.isdigit():
# 非法输入:包含非数字
print("[ ]")
return
heights.append(int(s))
# 特殊情况处理:全等或单个元素
if len(heights) <= 1:
print(" ".join(map(str, heights)))
return
# 调整身高顺序
result = adjust_heights(heights)
print(" ".join(map(str, result)))
except Exception:
# 异常处理
print("[]")
if __name__ == "__main__":
main()
算法原理解析
1. 高矮交替条件
if i % 2 == 0: # 高位
if heights[i] < heights[i + 1]:
# 需要交换
else: # 矮位
if heights[i] > heights[i + 1]:
# 需要交换
- 高位要求:位置0,2,4,…的元素 ≥ 下一个元素
- 矮位要求:位置1,3,5,…的元素 ≤ 下一个元素
- 允许相等:不触发交换条件
2. 贪心交换策略
heights[i], heights[i + 1] = heights[i + 1], heights[i]
swapped = True
- 只交换相邻元素:最小化单次移动距离(距离=1)
- 设置交换标志:表示需要重新检查前面位置
- 多轮处理:
while swapped
确保完全满足条件
3. 输入验证
for s in data:
if not s.isdigit():
print("[]")
return
- 检查每个字符是否为数字
- 包含非数字时输出空数组
[]
- 符合题目要求:非法输入返回空数组
4. 边界处理
- 空输入:直接返回空字符串
- 单元素数组:直接输出(无需处理)
- 全等数组:自动满足条件,不进行交换
示例解析
示例1:输入4 1 3 5 2
- 初始状态:
[4, 1, 3, 5, 2]
- 第一轮遍历:
- 位置0(高位):4≥1 ✓
- 位置1(矮位):1≤3 ✓
- 位置2(高位):3≤5 ✗ → 交换 →
[4,1,5,3,2]
- 位置3(矮位):3≥2 ✗ → 交换 →
[4,1,5,2,3]
- 第二轮遍历:
- 位置0:4≥1 ✓
- 位置1:1≤5 ✓
- 位置2:5≥2 ✓
- 位置3:2≤3 ✓
- 输出:
4 1 5 2 3
示例2:输入1 1 1 1 1
- 初始状态:
[1,1,1,1,1]
- 遍历检查:
- 所有位置都满足条件(1≥1 且 1≤1)
- 不进行任何交换
- 输出:
1 1 1 1 1
示例3:输入xxx
- 输入验证:包含非数字字符
x
- 直接输出:
[]
示例4:输入4 3 5 7 8
- 初始状态:
[4,3,5,7,8]
- 第一轮遍历:
- 位置0:4≥3 ✓
- 位置1:3≤5 ✓
- 位置2:5≤7 ✗ → 交换 →
[4,3,7,5,8]
- 位置3:5≤8 ✓
- 第二轮遍历:
- 位置0:4≥3 ✓
- 位置1:3≤7 ✓
- 位置2:7≥5 ✓
- 位置3:5≤8 ✓
- 输出:
4 3 7 5 8
总结与拓展
关键知识点
- 贪心算法应用:通过局部最优(相邻交换)实现全局最优
- 循环验证:多轮处理确保完全满足条件
- 边界处理:非法输入和特殊情况的处理
- 移动距离最小化:只进行相邻交换策略
拓展思考
- 优化方向:如何减少交换次数?
- 记录交换位置,下一轮从该位置开始
- 非相邻交换:允许非相邻交换是否能减少总移动距离?
- 分析:非相邻交换的移动距离更大,不推荐
- 双向处理:从左到右+从右到左交替处理
- 可能减少循环轮次
- 动态规划:O(n²)时间复杂度,记录最小移动路径
- 适用于更大数据集