题目分析
问题描述
小 Joan\texttt{Joan}Joan 有 NNN 个不同大小的积木,要在海滩上建造城市。一个城市就是一组建筑物的集合,其中:
- 单个积木放在沙子上可以视为一个建筑物
- 可以通过在积木上放置其他积木来建造更高的建筑物
- 每个积木上方最多只能直接放置一个积木
- 不允许将较大的积木放在较小的积木上(堆叠必须从上到下递减)
- 建筑物之间的顺序无关紧要
我们需要计算使用 NNN 个积木可以建造的不同城市的数量,记为 #(N)\#(N)#(N)。
问题转化
经过分析,这个问题可以转化为经典的集合划分问题:
- 每个建筑物对应一个堆叠(或单个积木),堆叠中积木的大小必须从上到下递减
- 这等价于每个建筑物是一个递减序列
- 城市就是对这些积木的一个划分,且划分中块的顺序无关
- 因此,#(N)\#(N)#(N) 实际上就是第 NNN 个贝尔数(Bell Number\texttt{Bell Number}Bell Number)
贝尔数 BnB_nBn 表示 nnn 个元素的集合的划分数。例如:
- B2=2B_2 = 2B2=2(对应题目中的 222 种城市配置)
- B3=5B_3 = 5B3=5
- B4=15B_4 = 15B4=15
解题思路
贝尔数的计算
贝尔数可以通过贝尔三角形来高效计算:
递推关系:
- 初始化:B(0,0)=1B(0,0) = 1B(0,0)=1
- 对于 n≥1n \geq 1n≥1:
- B(n,0)=B(n−1,n−1)B(n,0) = B(n-1,n-1)B(n,0)=B(n−1,n−1)
- B(n,k)=B(n,k−1)+B(n−1,k−1)B(n,k) = B(n,k-1) + B(n-1,k-1)B(n,k)=B(n,k−1)+B(n−1,k−1),其中 1≤k≤n1 \leq k \leq n1≤k≤n
- 贝尔数 Bn=B(n,0)B_n = B(n,0)Bn=B(n,0)
大数处理
由于贝尔数增长极快(B900B_{900}B900 是一个巨大的数字),我们需要使用大整数运算。为了优化内存和计算效率,我们采用:
- 100001000010000 进制表示:每个
int存储 000 到 999999999999 的数字 - 自定义大数加法:实现基于 100001000010000 进制的加法运算
- 动态规划存储:使用三维数组存储贝尔三角形的所有值
算法步骤
- 预计算所有 n≤900n \leq 900n≤900 的贝尔数
- 使用贝尔三角形递推关系
- 对于每个输入 nnn,直接输出预计算的结果
- 使用格式化输出确保数字正确显示
复杂度分析
- 时间复杂度:O(N2×L)O(N^2 \times L)O(N2×L),其中 N=900N = 900N=900,LLL 是大数的平均长度
- 空间复杂度:O(N2×L)O(N^2 \times L)O(N2×L),需要存储贝尔三角形的所有值
代码实现
// Bloques
// UVa ID: 10844
// Verdict: Accepted
// Submission Date: 2025-11-25
// UVa Run Time: 0.600s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
const int BASE = 10000;
vector<int> add(const vector<int>& a, const vector<int>& b) {
vector<int> res;
int carry = 0;
size_t len = max(a.size(), b.size());
for (size_t i = 0; i < len || carry; i++) {
if (i < a.size()) carry += a[i];
if (i < b.size()) carry += b[i];
res.push_back(carry % BASE);
carry /= BASE;
}
return res;
}
void printBigInt(const vector<int>& num) {
cout << num.back();
for (int i = num.size() - 2; i >= 0; i--) {
cout << setw(4) << setfill('0') << num[i];
}
}
int main() {
const int MAX_N = 900;
vector<vector<vector<int>>> bell(MAX_N + 1);
// 初始化贝尔三角形
bell[0].resize(1);
bell[0][0] = {1};
// 使用贝尔三角形递推计算所有贝尔数
for (int n = 1; n <= MAX_N; n++) {
bell[n].resize(n + 1);
// B(n,0) = B(n-1,n-1)
bell[n][0] = bell[n - 1][n - 1];
// B(n,k) = B(n,k-1) + B(n-1,k-1)
for (int k = 1; k <= n; k++) {
bell[n][k] = add(bell[n][k - 1], bell[n - 1][k - 1]);
}
}
// 处理输入输出
int n;
while (cin >> n && n != 0) {
cout << n << ", ";
printBigInt(bell[n][0]);
cout << endl;
}
return 0;
}
总结
本题的关键在于将积木城市的构建问题转化为贝尔数的计算问题。通过使用贝尔三角形和高效的大数运算,我们能够在合理的时间和空间内计算出 n≤900n \leq 900n≤900 的所有贝尔数。这种组合数学与算法优化的结合,展示了数学建模在解决实际问题中的重要性。
544

被折叠的 条评论
为什么被折叠?



