动态规划-有关计数问题的DP-多重集组合数
参考资料:
[1]:挑战程序设计竞赛(第二版)
自学笔记:
1.对书中变形公式“dp[ i ][ j ]=dp[ i ][ j-1 ]+dp[ i-1][ j ]-dp[ i-1][ j-1-a[i] ]”的理解
首先看一下暴力的解法:
设dp[i][j]表示从前i个物品中取出j个的总组合数;
易得状态转移方程为:dp[ i ][ j ]=∑min(a[i], j)k=0 dp[ i-1][ j-k];
根据状态转移方程很容易得出dp[ i ][ j-1 ]=∑min(a[i], j-1)k=0 dp[ i-1][ j-1-k ];
1 mem(dp,0); 2 dp[0][0]=1; 3 for(int i=1;i <= n;++i) 4 for(int j=0;j <= m;++j) 5 for(int k=0;k <= min(a[i],j);++k)//第i个物品取k个 6 dp[i][j] += dp[i-1][j-k];
①如果a[i] > j-1,那么k的取值为[0,j-1],那么dp[ i ][ j-1]=dp[ i-1 ][ j-1]+dp[ i-1 ][ j-1-1]+.......+dp[ i-1 ][0],如图紫框所示
因为a[i] > j-1,所以a[i] >= j,此时k的取值为[ 0,j ],也就是说dp[ i ][ j ]=dp[ i-1 ][ j ]+d[ i-1 ][ j-1]+.......+dp[ i-1 ][0],如图红框所示
此时,你会发现红框与紫框在[0,j-1]处是完全重合的,也就是说dp[ i ][ j ]=dp[ i ][ j-1]+d[ i-1 ][ j ];
②如果a[i] <= j-1,那此时k的取值为[0,a[i] ],那么dp[ i ][ j-1]=dp[ i-1 ][ j-1 ]+dp[ i-1 ][ j-2 ]+.......+dp[ i-1 ][ j-1-a[i] ],如图紫框所示
因为a[i] <= j-1,所以a[i] < j,此时k的取值为[ 0,a[i] ],也就是说dp[ i ][ j ]=dp[ i-1 ][ j ]+d[ i-1 ][ j-1]+.......+dp[ i-1 ][ j-a[i] ],如图红框所示
( X = j-1-a[i] )
在这种情况下,红框与紫框也有一部分是重合的,根据图示很容易得出dp[ i ][ j ]=dp[ i ][ j-1 ]+dp[ i-1 ][ j ]-dp[ i-1 ][ j-1-a[i] ];
所以,综上所述
1 void Solve() 2 { 3 mem(dp,0); 4 for(int i=1;i <= n;++i) 5 dp[i][0]=1;//一个都不取的方法只有一种 6 for(int i=1;i <= n;++i) 7 for(int j=1;j <= m;++j) 8 if(a[i] < j-1)//情况① 9 dp[i][j]=dp[i][j-1]+dp[i-1][j]; 10 else//情况② 11 dp[i][j]=dp[i][j-1]+dp[i-1][j]-dp[i-1][j-1-a[i]]; 12 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架