LastWhisper最后的耳语

计数DP --- 多重组合数问题

WKL && YZY·2020-07-04 11:41·486 次阅读

计数DP --- 多重组合数问题

计数DP --- 多重组合数问题#

多重集组合数,有n种物品,第 i 种有ai个。不同种类的物品可以互相区分但是相同的种类的无法区别。从这些物品中取m个有多少种取法。

限制条件

1n1000

1m1000

1ai1000

2M10000

这是一个比较经典的计数dp问题,我们设dp[i+1][j] := 从前i种物品中取出j个的组合总数。

暴力法思维量较小,应用递推。我们可以把从前i种物品取j个,划分为:从前i1种物品中取jk个,从剩下的第i种物品里取剩下的k个。则递推式可以表示为:

dp[i+1][j]=k=0min{j,a[i]}dp[i][jk]

但是利用这个递推式进行计算的时间复杂度为O(nm2),根据限制条件,该算法时间复杂度过高,需要进行优化。

我们对递推式进行优化,优化后的结果如下,下面将阐明如何得到这个递推式的:

dp[i+1][j]=dp[i+1][j1]+dp[i][j]dp[i][j1ai]

分两种情况:

  • 情况1: a[i]>j1

这时候dp[i+1][j1]k的取值范围是[0,j1]:

dp[i+1][j1]=dp[i][j1]+dp[i][j2]++dp[i][1]+dp[i][0]

又因为a[i]>j1a[i]j,这时k的取值范围是[0,j]:

dp[i+1][j]=dp[i][j]+dp[i][j1]++dp[i][1]+dp[i][0]

可以发现dp[i+1][j]dp[i+1][j1]之间只差一个dp[i][j]可以用如下图表示:

figure_1
  • 情况2:a[i]j1

这时,将式子进行展开得到:

{dp[i+1][j]=dp[i][j]+dp[i][j1]++dp[i][ja[i]]dp[i+1][j1]=dp[i][j1]++dp[i][j1a[i]]

用图像进行表示如下:

figure_2

因此,可以证明上述表达式成立。

核心代码如下:

Copy
int n, m; int a[MAX_N] int dp[MAX_n][MAX_M] void solve(){ for (int i = 0; i <= n; ++ i) dp[i][0] = 1; for (int i = 0; i < n; ++ i){ for (int j = 1; j <= m; ++ j){ if (j - 1 - a[i] >= 0){ dp[i + 1][j] = dp[i + 1][j - 1] + dp[i][j] - dp[i][j - 1 - a[i]]; }else dp[i + 1][j] = dp[i + 1][j - 1] + dp[i][j]; } } }

Update: 2020/8/16#

经典例题:POJ3046 - Ant Counting

posted @   Last_Whisper  阅读(486)  评论(1编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示
目录