洛谷题单指南-数学基础问题-P2638 安全系统

原题链接:https://www.luogu.com.cn/problem/P2638

题意解读:把a个红球、b个黑球放入n个盒子,求所有的方法。

解题思路:

盒子中可以放也可以不放,可以放任意个,因此,题目可以转化为将i个红球(0<=i<=a),j个黑球(0<=j<=b)放入n个盒子的方案数之和,

设f(n, i, j)表示将i个红球、j个黑球放入n个盒子的方案数,本题答案用代码片段表示为

for(int i = 0; i <= a; i++)
{
    for(int j = 0; j <= b; j++)
    {
        ans += f(n, i, j);
    }
}

问题就转换为如何计算f(n, i, j),也就是把i个黑球、j个红球放入n个盒子有多少种方法

设g(n, x)表示把x个球放入n个盒子有多少种方法,盒子可以为空

则根据乘法原理有:f(n, i, j) = g(n, i) * g (n, j)

现在问题集中在如何计算g(n, x),即把x个球放入n个盒子有多少种方法(盒子可以为空)?

问题进一步转换为:把x + n个球放入n个盒子有多少种方法(每个盒子至少有1个球)?

这就是经典的插板法:

x + n个球中间有x + n - 1个空隙,可以插n - 1块板,方案数为C(x + n - 1, n - 1)

如上,问题得解。

100分代码:

#include <bits/stdc++.h>
using namespace std;

int n, a, b;

unsigned long long c[55][55], ans;

int main()
{
    cin >> n >> a >> b;

    //初始化组合数
    for(int i = 0; i < 50; i++)
    {
        for(int j = 0; j <= i; j++)
        {
            if(j == 0) c[i][j] = 1;
            else c[i][j] = c[i - 1][j] + c[i - 1][j - 1];
        }
    }

    for(int i = 0; i <= a; i++)
    {
        for(int j = 0; j <= b; j++)
        {
            ans += c[i + n - 1][n - 1] * c[j + n - 1][n - 1];
        }
    }
    cout << ans;

    return 0;
}

仍有一个测试点过不了,大概率是超出unsigned long long,需要上高精度来计算方案数了。

高精度代码:

#include <bits/stdc++.h>
using namespace std;

vector<int> add(vector<int> &a, vector<int> &b)
{
    vector<int> result;
    int t = 0; //进位
    for(int i = 0; i < a.size() || i < b.size(); i++)
    {
        if(i < a.size()) t += a[i];
        if(i < b.size()) t += b[i];
        result.push_back(t % 10);
        t /= 10;
    }
    if(t) result.push_back(t);
    
    return result;
}

vector<int> mul2(vector<int> &a, vector<int> &b)
{
    vector<int> result(a.size() + b.size()); // 默认初始化为0
    for(int i = 0; i < a.size(); i++)
    {
        for(int j = 0; j < b.size(); j++)
        {
            result[i + j] += a[i] * b[j];
            result[i + j + 1] += result[i + j] / 10;
            result[i + j] %= 10;
        }
    }
    while (result.size() > 1 && result.back() == 0) result.pop_back(); 
    return result;
}

int n, a, b;
vector<int> c[200][200]; 
vector<int> ans;

int main()
{
    // 提升IO效率
    ios::sync_with_stdio(0);
    cin.tie(0);

    cin >> n >> a >> b;

    // 初始化组合数 (Pascal三角)
    for(int i = 0; i < 50; i++)
    {
        for(int j = 0; j <= i; j++)
        {
            if(j == 0) {
                // 相当于 c[i][j] = 1;
                c[i][j].push_back(1); 
            }
            else {
                // 相当于 c[i][j] = c[i - 1][j] + c[i - 1][j - 1];
                // 注意:如果 j==i,c[i-1][j]是个空vector,add函数也能完美兼容不报错
                c[i][j] = add(c[i - 1][j], c[i - 1][j - 1]);
            }
        }
    }

    ans.push_back(0); // 初始化 ans = 0

    for(int i = 0; i <= a; i++)
    {
        for(int j = 0; j <= b; j++)
        {
            // 相当于 temp = c[i + n - 1][n - 1] * c[j + n - 1][n - 1];
            vector<int> temp = mul2(c[i + n - 1][n - 1], c[j + n - 1][n - 1]);
            // 相当于 ans += temp;
            ans = add(ans, temp);
        }
    }

    // 逆序输出答案(因为低位存在数组前面)
    for(int i = ans.size() - 1; i >= 0; i--) {
        cout << ans[i];
    }
    cout << "\n";

    return 0;
}

 

posted @ 2024-04-09 16:35  hackerchef  阅读(149)  评论(0)    收藏  举报