動態規劃之神奇的口袋

2021-08-18 11:32:45 字數 1491 閱讀 4244

有乙個神奇的口袋,總的容積是40,用這個口袋可以變出一些物品,這些物品的總體積必須是40。

john現在有n(1≤n ≤ 20)個想要得到的物品,每個物品的體積分別是a1, a2……an。 john可以從這些物品中選擇一些,如果選出的物體的總體積是40,那麼利用這個神奇的口袋, john就可以得到這些物品。現在的問題是, john有多少種不同的選擇物品的方式。

輸入 輸入的第一行是正整數n (1 <= n <= 20),表示不同的物品的數目。接下來的n行,每行有乙個1到40之間的正整數,分別給出a1, a2……an的值。

輸出 輸出不同的選擇物品的方式的數目。

輸入樣例

3 20

20 20

輸出樣例

3設c[n][h]為n個物品可以選出重量h的方式數目。ai為第i個物品的重量。那麼c[k][m]表示前k個物品可以選出重量m的方式數目,那麼我們可以考慮有沒有必要選第k個物品:

一方面,如果必須選擇第k個物品,那麼也就意味著「第k個物品是組成重量為m所必須的」。換句話說,拿了第k個物品組成重量m的方式數目與不拿第k個物品所組成重量為m-a[k]的方式的數目是一樣的。此時c[k][m] = c[k-1][m-a[k]]。

另一方面,如果不必選擇第k個物品,也就意味著第k個物品的重量對於組成m重量沒有任何貢獻,那麼不要它也是一樣的,此時c[k][m] = c[k-1][m]。

所以結合兩反面考慮,有如下遞迴公式:

c[k][m] = c[k-1][m-a[k]] + c[k-1][m]

這種思路的本質實際上是列舉出對每乙個物品而言,要與不要對於整體的作用。題目上說物品數目的範圍是1到20,所以總共有2^20中情況。其中有一些情況實際上是很不必要的,我們可以篩選一下,縮小範圍,專業術語叫做「剪枝」。這裡用動態規劃寫出優化後的**。

時間複雜度為o(2^n)。

#include 

int a[21];

int pocket(int k, int m)

if (k <= 0)

return pocket(k-1, m-a[k]) + pocket(k-1, m); //pocket(k-1, m-a[k])為必須要第k個物品時可選方案數

//pocket(k-1, m)為不必要第k個物品時可選方案數

}int main()

printf("%d\n", pocket(n, 40));

return

0;}

將遞迴化為遞推,時間複雜度降為o(n*h)。

#include

#define h 40

int n;

int a[21];

int ways[21][h];

int pocket()}}

return ways[n][h];

}int main()

ways[0][0] = 1;

printf("%d\n", pocket());

}

神奇的口袋(動態規劃)

有乙個神奇的口袋,總的容積是40,用這個口袋可以變出一些物品,這些物品的總體積必須是40。john現在有n個想要得到的物品,每個物品的體積分別是a 1,a 2 a n。john可以從這些物品中選擇一些,如果選出的物體的總體積是40,那麼利用這個神奇的口袋,john就可以得到這些物品。現在的問題是,j...

神奇的口袋(動態規劃)

有乙個神奇的口袋,總的容積是40,用這個口袋可以變出一些物品,這些物品的總體積必須是40。john現在有n個想要得到的物品,每個物品的體積分別是a 1,a 2 a n。john可以從這些物品中選擇一些,如果選出的物體的總體積是40,那麼利用這個神奇的口袋,john就可以得到這些物品。現在的問題是,j...

2755 神奇的口袋(遞迴 動態規劃)

總時間限制 10000ms 記憶體限制 65536kb 描述有乙個神奇的口袋,總的容積是40,用這個口袋可以變出一些物品,這些物品的總體積必須是40。john現在有n個想要得到的物品,每個物品的體積分別是a1,a2 an。john可以從這些物品中選擇一些,如果選出的物體的總體積是40,那麼利用這個神...