題目:
有N個雞蛋和M個籃子,把雞蛋放到M個籃子里,每個籃子都不能為空。另外,需要滿足:任意一個小於N的正整數,都能由某幾個籃子內蛋的數量相加的和得到。寫出程序,使得輸入一個(N,M),輸出所有可能的分配情況。
從題意中應該可以得出,對於(1,1,2,2)和(1,2,1,2)這兩種組合,應該是一樣的。
因而對於這M個籃子中的雞蛋數量,我們用數組basket[M]來表示,我們按照非遞減順序進行排列,即basket[i] <= basket[i+1]
1.我們利用歸納法來總結出一個規律:
對於前n個籃子,其雞蛋數量總和為Sn,那么對於第n+1個籃子,其雞蛋數量應該滿足:
basket[n+1] <= Sn + 1,如果basket[n+1] > Sn + 1,那么Sn + 1這個數將無法通過相應的籃子雞蛋數相加來獲得。
由於是非遞減序列,因而
basket[n] <= basket[n+1] <= Sn + 1
2.我們來證明符合上式的數組能夠滿足條件“任意一個小於N的正整數,都能由某幾個籃子內蛋的數量相加的和得到”。
當M = 1時,basket[0] = 1,當M=2時,取basket[1] = 1,能夠滿足上述條件。
取basket[1] = 2,也能夠滿足上述條件。
假設M = n-1時,滿足上述條件,我們來證明當M = n時亦滿足。
前n-1個籃子的雞蛋數量總和為Sn-1,此時加上第n個籃子,總和為Sn = Sn-1 + basket[n-1]。即證明Sn - 1,Sn - 2,Sn - 3,Sn - (basket[n-1] - 1)都可以由某幾個籃子內蛋的數量相加的和得到。由於basket[n-1] <= Sn-1,而且小於或者等於Sn-1的數能由某幾個籃子內蛋的數量相加的和得到,所以Sn - 1,Sn - 2,Sn - 3,Sn - (basket[n-1] - 1)亦可得到。
證畢。
3.對於N和M的值,我們在輸入后即可做一個判斷。
2.1 當N < M,明顯有籃子為空,因而不符。
2.2 當N >= M時,第一個籃子必然要放1個雞蛋,其后面的籃子我們按照basket[n] <= basket[n+1] <= Sn + 1取最大值,依次為2,4,8,16......,雞蛋總數為2^M - 1,即M個籃子的雞蛋數量最大值。
所以M <= N < 2^M
4.代碼要點
4.1 對於函數
void solve(int current_sum, int basket_id, int current_num, int* basket, int N, int M)
其中current_sum表示當前所有籃子雞蛋的總和,
basket_id表示當前籃子的序號,
current_num表示將要放到當前籃子去的雞蛋數量,
basket, N, M值都是main函數中的原值,在遞歸中這三個參數基本沒變。
初始化為(0, 0, 1, basket, N, M)表示此時所有雞蛋數量為0,將要把1個雞蛋放進第0個籃子里面。
4.2 對於函數solve中的
if ((current_sum + current_num*(M - basket_id)) > N || (current_sum + (current_sum + 1)*((1<<(M - basket_id)) - 1)) < N) return;
我們采用的是搜索中的剪枝技術,即在每次遞歸時,通過預先判斷來看此路是否走得通。
(current_sum + current_num*(M - basket_id)) > N 表示之后的所有籃子都添加最小雞蛋數量,如果這都大於N,那么肯定不符。
(current_sum + (current_sum + 1)*((1<<(M - basket_id)) - 1)) < N 表示之后的所有籃子都添加相應的最大值,如果這都小於N,那么肯定也不符。
其中(current_sum + 1)*((1<<(M - basket_id)) - 1) 可以這樣解釋:
假設前面的籃子總和為n,那么緊挨着的后一個籃子里雞蛋數量最大值為n+1,其后的一個籃子最大值為n + (n + 1) + 1 = 2n + 2,這之后的一個籃子的最大值為n + (n + 1) + (2n + 2) + 1 = 4n + 4......(即這里取的都是Sn + 1)
依次類推,我們發現n + 1 + (2n + 2) + (4n + 4) + ...... = (2^count - 1)*(n + 1),count表示相應的籃子數量。
5.代碼如下:

1 #include <stdio.h> 2 #include <stdlib.h> 3 4 void solve(int current_sum, int basket_id, int current_num, int* basket, int N, int M) 5 { 6 if (current_sum == N && basket_id == M) 7 { 8 int i; 9 for (i = 0; i < M; i++) 10 printf("%d\t", basket[i]); 11 printf("\n"); 12 return; 13 } 14 15 if (current_num > N || basket_id >= M) 16 return; 17 18 if ((current_sum + current_num*(M - basket_id)) > N || 19 (current_sum + (current_sum + 1)*((1<<(M - basket_id)) - 1)) < N) 20 return; 21 22 int j; 23 for (j = current_num; j <= current_sum + 1; j++) 24 { 25 basket[basket_id] = j; 26 solve(current_sum + j, basket_id + 1, j, basket, N, M); 27 } 28 } 29 30 int main() 31 { 32 int N;//the number of eggs 33 int M;//the number of baskets 34 while (scanf("%d%d", &N, &M) != EOF) 35 { 36 if (N < M || N >= 1<<M || M <= 0) 37 printf("Wrong data!\n"); 38 else 39 printf("The combinations are as below:\n"); 40 41 int* basket = (int*)malloc(sizeof(int)*M); 42 solve(0, 0, 1, basket, N, M); 43 free(basket); 44 } 45 return 0; 46 }
1 #include <stdio.h> 2 3 void solve(int current_sum, int basket_id, int current_num, int* basket, int N, int M) 4 { 5 if (current_sum == N && basket_id == M) 6 { 7 int i; 8 for (i = 0; i < M; i++) 9 printf("%d\t", basket[i]); 10 printf("\n"); 11 return; 12 } 13 14 if (current_num > N || basket_id >= M) 15 return; 16 17 if ((current_sum + current_num*(M - basket_id)) > N || 18 (current_sum + (current_sum + 1)*((1<<(M - basket_id)) - 1)) < N) 19 return; 20 21 int j; 22 for (j = current_num; j <= current_sum + 1; j++) 23 { 24 basket[basket_id] = j; 25 solve(current_sum + j, basket_id + 1, j, basket, N, M); 26 } 27 } 28 29 int main() 30 { 31 int N;//the number of eggs 32 int M;//the number of baskets 33 while (scanf("%d%d", &N, &M) != EOF) 34 { 35 if (N < M || N >= 1<<M || M <= 0) 36 printf("Wrong data!\n"); 37 else 38 printf("The combinations are as below:\n"); 39 40 int* basket = new int[M]; 41 solve(0, 0, 1, basket, N, M); 42 delete[] basket; 43 } 44 return 0; 45 }