一道题目初探组合数学与DP关系,可重集组合公式推导

4002. 构造数组 (揭示了DP的本质是组合数学)


现在需要构造一对数组 ( a , b ) (a,b) (a,b),要求:

  • 数组 a a a 和数组 b b b 的长度都为 m m m
  • 两个数组中的元素的取值范围都是 [ 1 , n ] [1,n] [1,n]
  • ∀ i ∈ [ 1 , m ] ∀i∈[1,m] i[1,m] a i ≤ b i ai≤bi aibi
  • 数组 a a a 中元素非严格单调递增。
  • 数组 b b b 中元素非严格单调递减。

请问,共能构造出多少对满足条件的数组?

输出对 1 0 9 + 7 10^9+7 109+7 取模后的结果。


乍一看条件非常的多,感觉很难。

方法:数形结合,乘法原理(分步骤考虑)

只要在纸上随便画下 a a a b b b 就能发现问题可以转化成:

  1. n n n 个数中(每个数可以重复选)选 2 ∗ m 2*m 2m 个数,对这些数排列,大的一半给 b b b ,另一半给 a a a
  2. 分析 a a a 中的 m m m 个数,有多少种分配方案?

解答 2 :发现给定的 m m m 个数确定之后,方案是唯一的。

所以问题等价于 1 的方案数(经典可重集组合问题),答案是 C ( 2 m + n − 1 , n − 1 ) C(2m+n-1,n-1) C(2m+n1,n1)


数学推导:(思路,已知 C ( n , m ) C(n,m) C(n,m) 表示从 n n n不同的数中取 m m m 个的方案数)。

以下为数学中的模板思维套路:

  1. x i x_i xi 表示第 i i i 个数取多少数。有 ∑ i = 1 n x i = 2 ∗ m , x i ≥ 0 \sum_{i=1}^{n} x_i =2*m,x_i \ge0 i=1nxi=2m,xi0
  2. x i ′ = x i + 1 x'_i = x_i +1 xi=xi+1 ∑ i = 1 n x i = 2 ∗ m + n , x i > 0 \sum_{i=1}^{n} x_i =2*m+n,x_i > 0 i=1nxi=2m+n,xi>0
  3. 问题转化为给定 2 ∗ m + n 2*m+n 2m+n 个小球,用 n − 1 n-1 n1 个隔板将所有小球分成 n n n 部分,每部分小球的数量必须 大于 0 0 0 。因为 x i > 0 x_i>0 xi>0 ,所以隔板有 2 ∗ m + n − 1 2*m+n-1 2m+n1 个位置,又因为每个隔板要不同,所以可以用 C ( 2 m + n − 1 , n − 1 ) C(2m+n-1,n-1) C(2m+n1,n1) 表示。得证。

一个小TRICK C ( 2 m + n − 1 , n − 1 ) C(2m+n-1,n-1) C(2m+n1,n1) = C ( 2 m + n − 1 , 2 m ) C(2m+n-1,2m) C(2m+n1,2m) 哪个数据范围小用哪个


组合数怎么求? C ( n , m ) = C ( n − 1 , m − 1 ) + C ( n − 1 , m ) C(n,m) = C(n-1,m-1) + C(n-1,m) C(n,m)=C(n1,m1)+C(n1,m) 这不就是 0/1背包的推导方式吗?


所以: C ( 2 m + n − 1 , n − 1 ) C(2m+n-1,n-1) C(2m+n1,n1) 可以用 0 / 1 0/1 0/1 背包。 2 m + n − 1 2m+n-1 2m+n1 是背包的体积,每个元素的体积是 1 1 1

原来 DP 可以求方案数是因为 DP 的底层逻辑是组合数学,组合数学中的乘法原理加法原理可以求方案数。

那么一个明显的思路,可重集求方案数是不是可以直接用完全背包呢?答案是显然的!

以下给出两种背包求同一问题的代码。

0/1背包
f[0]=1;
for(int i=1;i<=2*m+n-1;i++){
    for(int j=2*m;j>=1;j--){
        f[j]= ((f[j] + f[j-1]) + M )%M;
    }
}
cout<<f[2*m]<<endl;
完全背包
f[0]=1;
for(int i=1;i<=n;i++){
    for(int j=1;j<=2*m;j++){
        f[j]= ((f[j] + f[j-1]) + M )%M;
    }
}
cout<<f[2*m]<<endl;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值