背包问题——“01背包”最优方案总数的求解

来源:互联网 发布:厦门大学网络教育 编辑:程序博客网 时间:2024/06/05 08:11

回忆一下01背包的动态规划状态及状态方程:

 设背包容量为M,一共有N件物品,每件物品质量为weight[i],每件物品的价值为value[i]。

1) 子问题定义:dp[i][j]表示前i件物品中选取若干件物品放入剩余容量为j的背包中所能得到的最大价值。

2) 根据第i件物品放或不放进行决策。

 

详细细节就不再多说了,我们直接进入正题。

在这里的话,最优方案总数就是物品总价值最大的方案数,那要怎么求呢?

 

我们设kry[i][j]dp[i][j]的方案数,那么最优方案总数就是kry[N][M],我们分析下怎么求kry[i][j],对于01背包来说:

 

经过max函数取最大值后,如果dp[i][j]==dp[i-1][j] 或者 dp[i][j]==dp[i-1][j-weight[i]]+value[i] ,那么kry[i][j] = kry[i-1][j] 或者 kry[i][j] = kry[i-1][j-weight[i]]。也就是说,对于dp[i][j]==dp[i-1][j] 时,表明第i件不放入时价值更大,那么到状态[i][j]的方案数就应该等于到状态[i-1][j]的方案数。同理,如果dp[i][j]==dp[i-1][j-weight[i]]+value[i],说明第i件放入时价值更大,此时到状态[i][j]的方案数就应该等于到状态[i-1][j-weight[i]]的方案数。

 

而若dp[i-1][j] ==dp[i-1][j-weight[i]]+value[i]],则说明即可以通过状态[i-1][j]在不加入第i件物品情况下到达状态[i][j],又可以通过状态[i-1][j-C[i]]在加入第i件物品的情况下到达状态[i][j],并且这两种情况都使得价值最大且这两种情况是互斥的,那么此时kry[i][j] = kry[i-1][j] + kry[i-1][j-weight[i]](你可以这样理解:两种到目前的状态[i][j]的方案都是最优解,那最优解的方案总数就是两个最优解的方案数相加)。

 

还有一点需要注意的,就是kry[][]必须全部初始化为1。因为无论dp[i][j]i, j的值是多少,dp[i][j]总是存在的,既然存在,那就说明至少有一种方案,因此初始化为1。这里可能有些难以理解,没事,先记着,慢慢就会懂了。

 

“Talk is cheap, show me the code.”

 

for(int j=0; j<maxm; j++ )    kry[j] = 1;memset(dp, 0, sizeof(dp));for( int i=1; i<=n; i++ ){    for( int j=1; j<=m; j++ )    {    dp[i][j] = dp[i-1][j];    kry[i][j] = kry[i-1][j];    if( j>=w[i] ){    if( dp[i][j]<dp[i-1][j-w[i]]+1 )    {    dp[i][j] = dp[i-1][j-w[i]] + 1;        kry[i][j] = kry[i-1][j-w[i]];    }    else if( dp[i][j]==dp[i-1][j-w[i]]+1 )             kry[i][j] = kry[i-1][j] + kry[i-1][j-w[i]];}    }}

同理,也可以用一维重复数组来求最优方案总数:

 

fill(kry, kry+maxm, INF );memset(dp, 0, sizeof(dp));for( int i=1; i<=n; i++ ){    for( int j=m; j>=w[i]; j-- )    {        if( dp[j]<dp[j-w[i]]+1)        {            dp[j] = dp[j-w[i]] + 1;            kry[j] = kry[j-w[i]];        }        else if( dp[j]==dp[j-w[i]]+1)            kry[j] = kry[j] + kry[j-w[i]];    }}

 

 


0 0