揹包問題——“01揹包”最優方案總數的求解

回憶一下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]];
    }
}

 

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章