每个数 = 头上两个数之和,两边永远 1——规则超简单,里面却藏着全部组合数!
想象一座数字金字塔:塔尖只有 1 个人;每往下一层多 1 个人;每个中间的人站在两个人正下方,他的数 = 头上两个人的和;每层的最左最右永远是 1。按这个规则搭出来:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
验证规则:2 = 1+1,3 = 1+2,6 = 3+3,4 = 1+3——每个中间数都是头上两数之和,两边永远 1 ✓。这座金字塔叫杨辉三角(中国古代数学家杨辉最先画出,西方叫帕斯卡三角)。
惊喜:第 5 行的 1 4 6 4 1,正好是 C(4,0)、C(4,1)、C(4,2)、C(4,3)、C(4,4)——杨辉三角就是「组合数查表」:不用手算 C(n,k),把三角搭起来,直接读第 n+1 行第 k+1 个数就行!
规则只有两句:① 每行最左最右都是 1;② 中间的数 = 它头上两个数的和。一行一行往下搭:
// 第 1 行:就一个数(首尾都是它自己) 1 // 第 2 行:首尾 1,中间没有 → 1 1 1 1 // 第 3 行:首尾 1,中间 = 1 + 1 = 2 1 2 1 // 第 4 行:首尾 1,中间 = 1+2=3、2+1=3 1 3 3 1 // 第 5 行:首尾 1,中间 = 1+3=4、3+3=6、3+1=4 1 4 6 4 1 // 规律:第 i 行有 i 个数;第 i 行第 j 个 = 第 i-1 行第 j-1 个 + 第 i-1 行第 j 个
a[i][j] = a[i-1][j-1] + a[i-1][j],用双重循环搭出前 6 行。用二维数组 a[行][位置](都从 1 开始数)存三角。规则翻译成代码语言:
// 递推(i = 行号,j = 行内位置) 每行首:a[i][1] = 1 每行尾:a[i][i] = 1 中间: a[i][j] = a[i-1][j-1] + a[i-1][j] // j 从 2 到 i-1 // 填表顺序:一行一行填(第 i 行只依赖第 i-1 行,上面填好才行) 第 1 行:a[1][1] = 1 第 2 行:a[2][1] = 1, a[2][2] = 1 第 3 行:a[3][1] = 1, a[3][3] = 1, a[3][2] = a[2][1]+a[2][2] = 2 第 4 行:a[4][1] = 1, a[4][4] = 1, a[4][2] = a[3][1]+a[3][2] = 3, a[4][3] = a[3][2]+a[3][3] = 3 第 5 行:a[5][1] = 1, a[5][5] = 1, a[5][2] = 1+3=4, a[5][3] = 3+3=6, a[5][4] = 3+1=4 第 6 行:1 5 10 10 5 1 // 按同样规则推出 // 伪代码 for i = 1 到 6: a[i][1] = 1 a[i][i] = 1 for j = 2 到 i-1: a[i][j] = a[i-1][j-1] + a[i-1][j] 输出第 i 行
把第 5 行和组合数并排放在一起:
// 第 5 行(n = 5)从左到右,就是 C(4,k),k = 0..4: 位置: 1 2 3 4 5 第5行: 1 4 6 4 1 C(4,k): C(4,0) C(4,1) C(4,2) C(4,3) C(4,4) = 1 = 4 = 6 = 4 = 1 // 逐个对上 ✓ // 一般规律:三角第 n 行第 k 个 = C(n−1, k−1)(行、位置都从 1 数) C(4,2) → 第 5 行第 3 个 = 6 C(6,3) → 第 7 行第 4 个 = 20 C(7,2) → 第 8 行第 3 个 = 21 // 为什么「搭三角」和「算组合数」得到同一张表? // 组合数也满足「上面两个加起来」的规律:C(n,k) = C(n−1,k−1) + C(n−1,k) // (从 n 人选 k 人:分成「必选甲」和「不选甲」两类,正好相加) // 递推一样、起点一样 → 表就一样。所以杨辉三角 = 组合数查表!
七级 C03 做过:m 行 n 列的格子,只能向右/向下,从左上到右下的路径数。转移方程是 dp[i][j] = dp[i−1][j] + dp[i][j−1](上面 + 左边),第一行第一列 = 1。把 3×3 格子(4×4 个点)的表填出来:
// 3×3 格子的 dp 表(七级 C03 手填过,角上 = 20) 1 1 1 1 1 2 3 4 1 3 6 10 1 4 10 20 // 对照杨辉三角——这些数全都在三角里: // 20 是三角第 7 行中间那个(第 7 行:1 6 15 20 15 6 1) // 10、6、4、3、2……也都能在第 4~6 行找到。 // 为什么?两个递推是同一个规则: 三角:a[i][j] = a[i-1][j-1] + a[i-1][j] // 上方两个相加 格子:dp[i][j] = dp[i-1][j] + dp[i][j-1] // 上面 + 左边相加 都是「当前 = 两个相邻来源之和」,起点也都是 1 → 表里的数相同(只是排法不同:格子按「斜线」排三角的数) // 一般结论:m 行 n 列的格子,左上→右下路径数 = C(m+n, m) = C(m+n, n) // 理由:一共走 m 步向下 + n 步向右 = m+n 步,从中选 m 步向下 → C(m+n, m) 3×3 格子:C(6,3) = 20 // 第 7 行中间 2×3 格子:C(5,2) = 10 2×2 格子:C(4,2) = 6
// 输入:n(2 ≤ n ≤ 7)N K(0 ≤ K ≤ N ≤ 6),一行三个数 // 输出:① 杨辉三角 n 行(金字塔形) // ② C(N,K) = 读三角第 N+1 行第 K+1 个 // ③ 3×3 格子(4×4 点)路径表 + 角上路径数,对照 C(6,3) // 示例:输入 6 4 2 → 6 行三角,C(4,2) = 6,格子角 20 #include <iostream> using namespace std; long long tri[16][16]; // tri[i][j]:三角第 i 行第 j 个(都从 1 开始) int main() { int n, N, K; cin >> n >> N >> K; // 1. 搭三角:首尾 1,中间 = 上方两数之和 for (int i = 1; i <= n; i++) { tri[i][1] = 1; tri[i][i] = 1; for (int j = 2; j < i; j++) tri[i][j] = tri[i-1][j-1] + tri[i-1][j]; } // 2. 输出 n 行(金字塔形:前面补空格) for (int i = 1; i <= n; i++) { for (int t = 0; t < n - i; t++) cout << " "; for (int j = 1; j <= i; j++) cout << tri[i][j] << " "; cout << endl; } // 3. 查表:C(N,K) 在第 N+1 行第 K+1 个 cout << "C(" << N << "," << K << ") = " << tri[N+1][K+1] << endl; // 4. 3×3 格子(4×4 点)路径表:每个 = 上 + 左,首行首列 = 1 long long f[5][5] = {}; for (int i = 1; i <= 4; i++) f[i][1] = 1; for (int j = 1; j <= 4; j++) f[1][j] = 1; for (int i = 2; i <= 4; i++) for (int j = 2; j <= 4; j++) f[i][j] = f[i-1][j] + f[i][j-1]; for (int i = 1; i <= 4; i++) { for (int j = 1; j <= 4; j++) cout << f[i][j] << " "; cout << endl; } // 注意:n 要 ≥ 7,三角第 7 行才搭好了 cout << "格子角 = " << f[4][4] << " = C(6,3) = " << tri[7][4] << endl; return 0; } // 动手验证:输入 7 6 3 → 7 行三角,C(6,3) = 20,格子角 20(两边一致) // 输入 6 5 2 → C(5,2) = 10