【算法&数据结构体系篇class39】卡特兰数

一、卡特兰数

卡特兰数又称卡塔兰数,英文名Catalan number是组合数学中一个常出现在各种计数问题中出现的数列。其前几项为:

1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452, ...

 

k(0) = 1, k(1) = 1时,如果接下来的项满足:

k(n) = k(0) * k(n - 1) + k(1) * k(n - 2) + ... + k(n - 2) * k(1) + k(n - 1) * k(0)

或者

k(n) = c(2n, n) - c(2n, n-1)

或者

k(n) = c(2n, n) / (n + 1)

说这个表达式,满足卡特兰数,常用的是范式123几乎不会使用到

二、题目一

假设给你N0,和N1,你必须用全部数字拼序列

返回有多少个序列满足:任何前缀串,1的数量都不少于0的数量

 

package class39;

import java.util.LinkedList;

/**
 * 假设给你N个0,和N个1,你必须用全部数字拼序列
 *
 * 返回有多少个序列满足:任何前缀串,1的数量都不少于0的数量
 *
 * 题型与成对括号是一样的。 有n个左括号,n个右括号 拼接全部字符()排序列 返回多少个满足的序列
 * 潜台词有效序列就是要 任何前缀串 左括号数量要不少于右括号的数量 比如 ())... 这里来到第三个字符 九出现右括号2个>左括号 就是无效的
 * 那么有效的排列是多少 根据排列组合  C(2N,N)  就是全部组合数 总共2n个字符,左括号n个 要在2n个的位置找n个 所以就是C 2N ,N
 * 那么无效的怎么算: 这里我们有个结论 就是 两个集合的数 分别都各自有一一对应的数 那么两集合就是相等数的
 * 我们将一个 无效的排序 在出现前缀串无效的后一个开始 后面的括号都取相反, 比如())(() 一开始是3个左右括号
 * 来到第三个 出现无效情况 ()),此时前缀肯you定是右括号个数=左括号个数+1
 * 接着后面的括号 (() 都取反 变成 ))( 这里也变成 右括号=左括号+1
 * 最终变成 右括号+1 = 左括号-1 大2个   而这种集合 就是一一对应无效排序的 所以算出 2n个中选N+1位置排列组合
 * 就是C(2N,N-1) 等价于无效排序组合是这么多
 * 那么有效的就是 C(2N,N) - C(2N,N+1)   来自公式一演算
 *
 *
 *
 * 这个题型是运用 卡特兰数的公式求解
 * k(0) = 1, k(1) = 1时,如果接下来的项满足:
 * k(n) = k(0) * k(n - 1) + k(1) * k(n - 2) + ... + k(n - 2) * k(1) + k(n - 1) * k(0)
 * 或者
 * k(n) = c(2n, n) - c(2n, n-1)
 * 或者
 * k(n) = c(2n, n) / (n + 1)
 * 就说这个表达式,满足卡特兰数,常用的是范式1和2,3几乎不会使用到
 */
public class Ways10 {

    //根据公式三直接算出结果
    public static long ways2(int n){
        //base case  0个 1个的时候 都能满足 有1种
        if(n < 0) return 0;
        if(n < 2)  return 1;

        //定义两个辅助变量
        long a = 1;
    
### 关于ACM竞赛中的模板封装 在ACM竞赛中,为了提高编码效率并减少错误率,通常会将常用的数据结构算法封装成模块化代码。以下是几个常见的模板封装示例及其说明。 #### 树状组(Binary Indexed Tree) 树状组是一种用于高效处理前缀和查询以及单点更新操作的数据结构。以下是一个基于C++的通用树状组模板: ```cpp template&lt;typename T&gt; class BIT { #define lowbit(a) (a &amp; (-a)) private: int size; T* tree; public: BIT(int n) : size(n), tree(new T[size + 1]{}) {} ~BIT() { delete[] tree; } void update(int idx, T delta) { while (idx &lt;= size) { tree[idx] += delta; idx += lowbit(idx); } } T query(int idx) const { T res = 0; while (idx &gt; 0) { res += tree[idx]; idx -= lowbit(idx); } return res; } #undef lowbit }; ``` 此实现支持动态范围内的加法更新和区间求和查询[^1]。 #### 卡特兰计算模板 卡特兰是一类组合计问题,在许多场景下都有应用。下面展示了一个模 \(10^9+7\) 的快速计算第n项卡特兰的方法: ```cpp #include &lt;bits/stdc++.h&gt; using namespace std; const long long MOD = 1e9 + 7; long long inv[1000010]; void preprocess(long long limit) { inv[1] = 1; for (int i = 2; i &lt;= limit; ++i) { inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD; } } long long catalan_number(long long n) { if (n == 0 || n == 1) return 1; long long prev = 1, curr = 0; for (int k = 2; k &lt;= n; ++k) { curr = ((prev * (4LL * k - 2)) % MOD * inv[k + 1]) % MOD; prev = curr; } return prev; } ``` 该函通过预处理逆元来加速乘法运算,并利用递推关系有效减少了时间复杂度[^3]。 #### 背包问题模板 对于经典的背包问题,可以采用如下方式定义其基本框架: ```java import java.util.*; import java.io.*; public class KnapsackProblem { static final int MAX_N = 101; static final int MAX_W = 10001; public static void main(String args[]) throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String line = br.readLine(); int n = Integer.parseInt(line.trim()); int[] weights = Arrays.stream(br.readLine().split(&quot; &quot;)) .mapToInt(Integer::parseInt).toArray(); int[] values = Arrays.stream(br.readLine().split(&quot; &quot;)) .mapToInt(Integer::parseInt).toArray(); int[][] dp = new int[n+1][MAX_W]; for(int i=1;i&lt;=n;i++){ for(int w=weights[i-1];w&lt;MAX_W;w++){ dp[i][w] = Math.max(dp[i-1][w],dp[i-1][w-weights[i-1]]+values[i-1]); } } System.out.println(Arrays.toString(dp[n])); } } ``` 这段代码实现了标准的0/1背包问题解决逻辑,适用于物品量较少的情况[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值