1. 程式人生 > >AlvinZH雙掉坑裏了

AlvinZH雙掉坑裏了

可能 reserve string all print 數據包 down 迷宮 blog

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份,69-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份,89-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雙掉坑裏了