动态规划-有关计数问题的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 }
posted @ 2018-11-08 14:48  HHHyacinth  阅读(705)  评论(0编辑  收藏  举报