AlvinZH雙掉坑裏了
AlvinZH雙掉坑裏了
時間限制: 1000 ms 內存限制: 65536 kb
題目描述
AlvinZH雙掉進坑裏了!
幸運的是,這坑竟然是寶藏迷宮的入口。這一次AlvinZH機智地帶了很多很多背包——裝金幣!
假設現在AlvinZH撿到了n塊金幣,他一共帶了m個背包,每個背包可以裝任意多金幣,但AlvinZH不允許有空的背包。
請你幫他計算一下一共有多少種裝金幣的方法吧!
註意:所有背包看作相同,即{1,3}和{3,1}是同一種方法。
輸入
輸入包含多組數據。
每組數據包含兩個正整數,為金幣數n(1≤n≤10^4),背包數m(1≤m≤10^3,且m≤n)。
輸出
對於每組數據,輸出一行,為使用所有背包裝金幣的方法數(結果對1000007取模)。
輸入樣例
4 2
9 3
輸出樣例
2
7
樣例解釋
4:{1,3}{2,2};
9:{1,1,7}{1,2,6}{1,3,5}{1,4,4}{2,2,5}{2,3,4}{3,3,3}。
HINT
這不是簡單的背包問題,請勿套公式。
AlvinZH:其實和背包沒有任何關系~
思路
簡單DP。簡化問題:將n個金幣放入m個盒子,無空盒。 直接上dp吧,dp[i][j]:將i個金幣放入j個盒子的方法數。此題的關鍵在於如何找到狀態轉移方程,很有可能會計算重復的方法。我們把答案分成兩部分: ①放完之後所有盒子金幣數量大於1; ②放完之後至少有一個盒子金幣數量為1。 這樣分可以保證不會有重復計算。狀態轉移方程: dp[i][j]=dp[i?j][j]+dp[i?1][j?1]。 ① dp[i?j][j] :將(i-j)個金幣放到j個盒子,然後這j個盒子每個再放1個金幣。表示的是將i個金幣分成所有盒子金幣數量大於1的方案總數。例如,求9分解成3份,6(9-3)分成3份可以分為{1,1,4}{1,2,3}{2,2,2},則9可以分為{2,2,5}{2,3,4}{3,3,3},共3種。 ② dp[i?1][j?1]:將(i-1)個金幣放到(j-1)個盒子,再來一個盒子放1個。表示的是將i個金幣分成至少有一個盒子金幣數量為1的方案總數。例如,求9分解成3份,8(9-1)分成2份可以分為{1,7}{2,6}{3,5}{4,4},則9可以分為{1,1,7}{1,2,6}{1,3,5}{1,4,4},共4種。 難點在於如何避免重復,這裏處理得十分巧妙,請細細體會。
以上轉自https://www.cnblogs.com/AlvinZH/p/7840604.html#_label3
之所以將答案分為1,2兩種情況,是由於1,2兩種情況可以把原問題分為兩個不相交的集合,且每個子集的答案都可以以一種方式從規模更小的問題中生成。
放完後所有盒子金幣數大於1:假設”將i個金幣放入j個盒子且所有盒子金幣數大於1“的放法有k種,那麽我從每個盒子拿去一個金幣,一定對應子問題dp[i-j][j]的一種放法,即兩者的放法數是相等的。
每個dp[i-j][j]的一種放法,各加一個金幣都可以生成現有問題的一個放法;而現有問題的放法各拿去一個金幣又可以生成子問題dp[i-j][j]的一種放法,二者一一對應。
放完後至少有一個盒子金幣數量為1:假設”將i個金幣放入j個盒子且至少有一個盒子金幣數量為1“的放法有k種,那我拿去這個盒子和這個球,一定對應子問題dp[i-1][j-1]的一種放法,即二者放法數是相等的。
另外關於初始值,將某個非法子問題的dp值置0來使得其貢獻為0
i>=j才能保證沒有空包,只對i>=j問題求解,否則保持初始值0,表示該情況不能生成更規模問題的解(貢獻為0)。
另外i,j>=1的才是問題討論的範圍,因此循環i,j從1開始,且將dp[i][0],dp[0][j]也置0
但註意dp[0][0]要初始化為1(但感覺上應該是初始化do[1][1]=1,只不過初始化dp[0][0]=1碰巧能通過轉移方程求出dp[1][1]=1且這樣能保持循環的美觀,也可以直接初始化dp[1][1]但此時循環中i=1,j=1的情況就要跳過,否則又會被重新寫為0)
參考代碼
1 // 2 // Created by AlvinZH on 2017/10/23. 3 // Copyright (c) AlvinZH. All rights reserved. 4 // 5 6 #include <cstdio> 7 #include <cstring> 8 #define MOD 1000007 9 10 int n, m; 11 int dp[10005][1005]; 12 13 int main() 14 { 15 while(~scanf("%d %d", &n, &m)) 16 { 17 memset(dp, 0, sizeof(dp)); 18 dp[0][0] = 1; 19 for (int i = 1; i <= n; ++i) { 20 for (int j = 1; j <= m; ++j) { 21 if(i - j >= 0) 22 dp[i][j] = (dp[i-j][j] + dp[i-1][j-1]) % MOD; 23 } 24 } 25 26 printf("%d\n", dp[n][m]); 27 } 28 }
AlvinZH雙掉坑裏了