UVa 10844 Bloques

题目分析

问题描述

Joan\texttt{Joan}JoanNNN 个不同大小的积木,要在海滩上建造城市。一个城市就是一组建筑物的集合,其中:

  • 单个积木放在沙子上可以视为一个建筑物
  • 可以通过在积木上放置其他积木来建造更高的建筑物
  • 每个积木上方最多只能直接放置一个积木
  • 不允许将较大的积木放在较小的积木上(堆叠必须从上到下递减)
  • 建筑物之间的顺序无关紧要

我们需要计算使用 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 1n1
    • B(n,0)=B(n−1,n−1)B(n,0) = B(n-1,n-1)B(n,0)=B(n1,n1)
    • 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,k1)+B(n1,k1),其中 1≤k≤n1 \leq k \leq n1kn
  • 贝尔数 Bn=B(n,0)B_n = B(n,0)Bn=B(n,0)

大数处理

由于贝尔数增长极快(B900B_{900}B900 是一个巨大的数字),我们需要使用大整数运算。为了优化内存和计算效率,我们采用:

  1. 100001000010000 进制表示:每个 int 存储 000999999999999 的数字
  2. 自定义大数加法:实现基于 100001000010000 进制的加法运算
  3. 动态规划存储:使用三维数组存储贝尔三角形的所有值

算法步骤

  1. 预计算所有 n≤900n \leq 900n900 的贝尔数
  2. 使用贝尔三角形递推关系
  3. 对于每个输入 nnn,直接输出预计算的结果
  4. 使用格式化输出确保数字正确显示

复杂度分析

  • 时间复杂度O(N2×L)O(N^2 \times L)O(N2×L),其中 N=900N = 900N=900LLL 是大数的平均长度
  • 空间复杂度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 900n900 的所有贝尔数。这种组合数学与算法优化的结合,展示了数学建模在解决实际问题中的重要性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值