🃏 模拟发牌 - 算法可视化教学

📋 题目描述

题目:模拟发牌

n 张编号为 1 到 n 的牌,按顺序发给 m 个人。

发牌规则:

  • 第 1 张给第 1 个人
  • 第 2 张给第 2 个人
  • ...
  • 第 m 张给第 m 个人
  • 第 m+1 张给第 1 个人(循环回来)
  • 以此类推,直到发完所有牌

样例输入:

7 3

样例输出:

1 4 7
2 5
3 6

💡 输出说明:

第1行:第1个人得到的牌(1, 4, 7)

第2行:第2个人得到的牌(2, 5)

第3行:第3个人得到的牌(3, 6)

💡 核心思想

取模运算实现循环分配

第 k 张牌应该发给第几个人?

person = (k - 1) % m + 1

公式推导(以 m=3 为例):

k (k-1) % 3 +1 后 发给谁
101第1人
212第2人
323第3人
401第1人
512第2人
623第3人
701第1人

代码思路:

不使用额外存储,用双重循环:

  • 外层循环:遍历每个人 i(从1到m)
  • 内层循环:遍历所有牌 j(从1到n)
  • 判断:如果 j % m == i % m,则牌j属于人i

🎴 发牌演示

牌堆(当前牌高亮显示)

📊 执行过程

点击「开始演示」查看执行过程

🎯 算法总结

核心公式:

第 k 张牌 → 第 (k-1) % m + 1 个人

代码实现要点:

for (int i = 1; i <= m; i++) { // 遍历每个人 for (int j = 1; j <= n; j++) { // 遍历所有牌 if (j % m == i % m) { // 判断牌j是否属于人i cout << j << " "; } } cout << endl; }

复杂度分析:

  • 时间复杂度:O(n × m)
  • 空间复杂度:O(1)(不使用额外存储)

关键理解:

  • i % m 的取值范围是 0, 1, 2, ..., m-1
  • j % m == i % m 找到所有属于第i个人的牌
  • 取模运算是实现"循环"的利器

🔬 考点分析

考察知识点:

  • ✅ 取模运算(%)的理解和应用
  • ✅ 双重循环的使用
  • ✅ 循环变量的边界处理
  • ✅ 格式化输出(空格控制)

易错点:

  • ❌ 行末多余空格(需用标志变量控制)
  • ❌ 取模结果从0开始,与"第几人"的1开始不一致
  • ❌ 循环边界写错(从0开始还是从1开始)

拓展思考:

  • 如果牌数 < 人数,如何处理?
  • 如果要求输出每个人有多少张牌?
  • 如果牌的编号不是连续的?

举一反三:

取模运算的典型应用场景:

  • 循环报数(报n的倍数的人出列)
  • 约瑟夫环问题
  • 周期性问题(星期几计算)