1. 程式人生 > >POJ1742 coins 動態規劃之多重部分和問題

POJ1742 coins 動態規劃之多重部分和問題

變形 價值 span 維數 text 推出 遞推 pro cal

原題鏈接:http://poj.org/problem?id=1742

題目大意:tony現在有n種硬幣,第i種硬幣的面值為A[i],數量為C[i]。現在tony要使用這些硬幣去買一塊價格不超過m的表。他希望買的時候不用找零,問有多少種價格能滿足這一點。

這個問題實際上是一個多重部分和的問題:假設有n種物品,每種物品的價值為v[i],數量為c[i],隨意選取這些物品,能否使它們的價值之和恰好為m。使用動態規劃的思想來求解這類問題:

定義dp數組,dp[i][j]的值代表前i種物品隨意選取,價值之和為j時第i種物品最多能剩下多少個,當前i種物品無法使價值之和剛好為j時,其值為-1。那麽,很明顯,遞推關系存在四種情況:

①:如果dp[i - 1][j] >= 0,也就是說前i - 1種物品已經可以組合得到j,此時第i種物品可以一件也不取,即dp[i][j] = c[i];

②:如果不滿足情況①,那麽我們要通過在第i種物品中選取來使價值之和為j,但如果j的值小於v[i]的話,哪怕只選取一個也會超過j,所以此時無法實現要求,dp[i][j] = -1;

③:如果不滿足情況①且j又不小於v[i]時,也許我們可以通過選取a個第i種物品來使價值之和恰好為j(1 <= a)。如果可以的話,那麽選取a - 1個一定可以使價值之和為j - v[i],所以dp[i][j]可以由dp[i][j - v[i]]推出。如果

dp[i][j - v[i]]等於-1(無法實現)或者等於0(第i種物品已用完),dp[i][j]都等於-1

④:在情況③中,如果在第i種中拿a個能使價值之和為j - v[i],且第i種物品還有剩余,那再拿一個就可以使價值之和為j了,即dp[i][j] = dp[i][j - v[i]] - 1

有了這些遞推關系,我們就可以用如下的代碼來求解多重部分和問題:

int n;//物品種數
int m;//目標和
int v[n];//每種物品的價值
int c[n];//每種物品的數量
memset(dp, -1, sizeof(dp));
dp[0] = 0;//初始化
for(int i = 0;i < n;i++)
{
    
for(int j = 0;j <= m;j++) { if(dp[j] >= 0)//情況① { dp[j] = c[i]; //這裏滾動使用了一個一維數組,dp[j]在更新前代表的是上一輪的值(即二維的dp[i - 1][j])或初始值 } else if(j < v[i] || dp[j - v[i]] <= 0)//情況②、③ { dp[j] = -1; } else//情況④ { dp[j] = dp[j - v[i]] - 1; } } }

這道poj1742只是在上面這個問題的基礎上略加變形,我們只需要遍歷dp數組求出1-m中有多少中價格可以被組合出來即可得到答案。

不過這種解法在POJ上耗時1800ms左右,雖然可以ac,但可能無法應付更大的數據規模或者更高的效率要求,我們還可以借助二進制優化或者各種數據結構來進一步提高效率。

POJ1742 coins 動態規劃之多重部分和問題