洛谷题单指南-数学基础问题-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;
}
浙公网安备 33010602011771号