有乙個神奇的口袋,總的容積是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,那麼利用這個神...