題目:
有一個神奇的口袋,總的容積是40,用這個口袋可以變出一些物品,這些物品的總體積必須是40。John現在有n個想要得到的物品,每個物品的體積分別是a 1,a 2……a n。John可以從這些物品中選擇一些,如果選出的物體的總體積是40,那麼利用這個神奇的口袋,John就可以得到這些物品。現在的問題是,John有多少種不同的選擇物品的方式。
Input 輸入的第一行是正整數n (1 <= n <= 20),表示不同的物品的數目。接下來的n行,每行有一個1到40之間的正整數,分別給出a 1,a 2……a n的值。 Output 輸出不同的選擇物品的方式的數目。
Sample Input
3
20
20
20
Sample Output
3
思路:
這道題要求的是給出n個物品的重量,要求讓他們的重量和爲40時,有多少種情況。
剛開始看這道題的時候,覺得用搜索就可以寫,從重量爲40,物品數爲n開始往下找。兩種情況,一種是選擇這個物品,重量減去a[i], 一種是不選擇這個物品,重量不變,繼續往下找。
int dfs(int sum,int num)
{
if(sum == 0)
return 1;
if(num == 0)
return 0;
return dfs(sum-a[num],num-1)+dfs(sum,num-1); //取或者不取
}
當然,如果會用dp寫也是挺不錯的。對於dp大家對於01揹包這個還算了解吧,其實這個和它也差不多。在解決dp問題的時候,我們應該從後往前推,找到問題的來源,比如在這道題中,要求的是重量爲40時,有多少種情況,那麼我們可以利用二維數組dp[i][j], i表示的是重量,j表示物品的個數。 當然dp[i][j]的數值應該等於重量爲i,數值爲j-1的情況相同。 如果a[j]恰好和重量i相等,那麼dp[i][j]就加1,如果a[j]小於i時,那麼dp[i][j] += dp[i-a[j]][j-1]。其實這個等式表示的是重量爲i,數量爲j的情況個數,應該等於其本身的情況再加上重量爲 i-a[j],物品個數爲j-1的 情況。
for(int i = 1; i <= 40; i++) //表示物品的總重量
{
for(int j = 1; j <= n; j++) //表示前j種物品在重量爲i時,有幾種情況
{
dp[i][j] = dp[i][j-1]; //重量爲i, 前j-1個物品的情況個數
if(i == a[j])
dp[i][j]++; //情況加1
if(i > a[j])
dp[i][j] += dp[i-a[j]][j-1]; //現在的情況數加上 重量爲i-a[j]時,j-1個物品的情況數
}
}
我們也可以用一維數組來表示重量,其意義和上面一樣。代碼如下
for(int i = 1; i <= n; i++)
{
for(int j = 40; j >= 1; j--)
{
if(dp[j] && j+a[i] <= 40)
{
dp[j+a[i]] += dp[j];
}
}
dp[a[i]]++;
}