组合数


✅ 1. 几道经典的“组合数”算法题

✅ 2. 解释组合数算法及其优化方法(含快速求法)


📚 组合数经典算法题推荐:

1. Leetcode 77. 组合(Combinations) - 中等

给定两个整数 n 和 k,返回从 1 到 n 中选出 k 个数的所有组合。

  • 🔗 Leetcode 77

  • 🧠 考察:回溯 + 组合生成

  • class Solution {
    public:
        vector<vector<int>> res;
        vector<int> path;
        void dfs(int n, int k, int start) {
            if (path.size() == k) {
                res.push_back(path);
                return;
            }
            for (int i = start; i <= n; i++) {
                path.push_back(i);
                dfs(n, k, i + 1);
                path.pop_back();
            }
        }
        vector<vector<int>> combine(int n, int k) {
            dfs(n,k,1);
            return res;
        }
    };
    

2. Leetcode 62. 不同路径(Unique Paths) - 中等

一个机器人只能向右或下,从左上角走到右下角,共有多少种不同的路径?

  • 答案是组合数:C(m+n-2, n-1)

  • 🔗 Leetcode 62

  • 🧠 考察:组合数推导 + 优化

  • class Solution {
    public:
        /*
        int MOD=INT_MAX;
        long long c[2010][2010];
        void init_comb() {
            for (int i = 0; i < 2010; ++i) {
                c[i][0] = 1;
                for (int j = 1; j <= i; ++j)
                    c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % MOD;
            }
        }*///预处理,这道题并不推荐..
        int uniquePaths(int m, int n) {
            init_comb();
            long long resl = 1;
            int a = m + n - 2, b = min(m - 1, n - 1);
            for (int i = 1; i <= b; i++) {
                resl = resl * (a - i + 1) / i;
            }
            return c[a][b];
        }
    };
    

3. Leetcode 118. 杨辉三角 - 简单

给出前 n 行的杨辉三角(帕斯卡三角形),即组合数的二维结构。

  • 🔗 Leetcode 118

  • 🧠 考察:组合数递推公式:C(n, k) = C(n-1, k) + C(n-1, k-1)

  • class Solution {
    public:
        vector<vector<int>> generate(int numRows) {
            vector<vector<int>> triangle;
            for (int i = 0; i < numRows; i++) {
                vector<int> row(i + 1, 1);
                for (int j = 1; j < i; j++) {
                    row[j] = triangle[i - 1][j - 1] + triangle[i - 1][j];
                }
                triangle.push_back(row);
            }
            return triangle;
        }
    };
    

4. Leetcode 1641. 统计字典序元音字符串的数目 - 中等

给你一个整数 n,返回长度为 n、仅由元音字母组成、字典序非递减的字符串数量。

  • 实质是一个可重组合数问题:C(n+5−1, 5−1) = C(n+4, 4)

  • 🔗 Leetcode 1641

  • 🧠 考察:“可重组合”公式:C(n+k−1, k)

  • 从 5 个有序元素中选 n 个(可重复),且顺序不能下降,相当于有放回的组合 + 字典序约束

  • 相当于是 在 5 类物品中选 n 个,允许重复,且按顺序排列

  • class Solution {
    public:
        int countVowelStrings(int n) {
            int res = 1;
            for (int i = 1; i <= 4; i++) {
                res = res * (n + i) / i;
            }
            return res;
        }
    };
    

5. Leetcode 60. 第 k 个排列 - 困难

给定 n 和 k,返回第 k 个字典序排列。用到了组合数的思想来“跳过”某些分支。

  • 🔗 Leetcode 60

  • 🧠 考察:阶乘 + 剪枝跳转

  • class Solution {
    public:
        string getPermutation(int n, int k) {
            vector<int> nums;
            for (int i = 1; i <= n; i++)
                nums.push_back(i);
            vector<int> fact(n);
            fact[0] = 1;
            for (int i = 1; i < n; i++) {
                fact[i] = fact[i - 1] * i;
                //cout << fact[i] << endl;
            } // 求阶乘
            k--;
            string res;
            for (int i = n - 1; i >= 0; i--) {
                int idx = k / fact[i];
                res += to_string(nums[idx]);
                nums.erase(nums.begin() + idx);
                k %= fact[i];
            }
            return res;
        }
    };
    

📘 什么是组合数?

组合数 C(n, k) 表示从 n 个元素中选出 k 个的方式数。

✅ 公式定义:

C(n, k) = n! / (k! * (n - k)!)

⚡ 如何快速计算组合数?

🔹 方法 1:暴力公式法(适合小数)

long long comb(int n, int k) {
    long long res = 1;
    for (int i = 1; i <= k; ++i) {
        res = res * (n - i + 1) / i;
    }
    return res;
}
  • 优点:不需打表,计算快(但要注意除法顺序)

🔹 方法 2:打表预处理阶乘(适合多次查询)

const int N = 100005;
const int MOD = 1e9+7;
long long fac[N], inv[N];

// 快速幂
long long qpow(long long a, long long b) {
    long long res = 1;
    while(b) {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}

// 初始化
void init() {
    fac[0] = 1;
    for(int i = 1; i < N; ++i) fac[i] = fac[i-1] * i % MOD;
    inv[N-1] = qpow(fac[N-1], MOD - 2);
    for(int i = N - 2; i >= 0; --i)
        inv[i] = inv[i + 1] * (i + 1) % MOD;
}

// 查询组合数
long long C(int n, int k) {
    if (k < 0 || k > n) return 0;
    return fac[n] * inv[k] % MOD * inv[n - k] % MOD;
}
  • 优点:快速求组合数 + 模数取余
  • 用于大规模组合数运算场景(如竞赛、字符串问题等)

如果你希望练习组合数生成 + 回溯算法

再推荐一个:

6. Leetcode 39. 组合总和 - 中等

找出所有可行的加和组合(元素可重复使用)

  • 🔗 Leetcode 39

  • 🧠 考察:DFS + 剪枝 + 回溯

  • class Solution {
    public:
        vector<vector<int>> res;
        vector<int> path;
        void dfs(vector<int>& candidates, int target, int start) {
            if (target == 0) {
                res.push_back(path);
                return;
            }
            for (int i = start; i < candidates.size(); ++i) {
                if (candidates[i] > target) continue; 
    
                path.push_back(candidates[i]);
                dfs(candidates, target - candidates[i], i);//可以重复使用当前的数 
                path.pop_back(); 
            }
        }
        vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
            sort(candidates.begin(), candidates.end()); 
            dfs(candidates, target, 0);
            return res;
        }
    };
    
    

posted @ 2025-06-03 20:49  W_K_KAI  阅读(39)  评论(0)    收藏  举报