【遞歸練習】放蘋果

http://www.cnblogs.com/dongsheng/archive/2012/08/15/2640468.htmlhtml

Descriptionios

把M個一樣的蘋果放在N個一樣的盤子裏,容許有的盤子空着不放,問共有多少種不一樣的分法?(用K表示)5,1,1和1,5,1 是同一種分法。

Input算法

第一行是測試數據的數目t(0 <= t <= 20)。如下每行均包含二個整數M和N,以空格分開。1<=M,N<=10。

Output學習

對輸入的每組數據M和N,用一行輸出相應的K。

Sample Input測試

1
7 3

Sample Outputspa

8


 

 

/* 功能Function Description:     POJ-1664
   開發環境Environment:          DEV C++ 4.9.9.1
   技術特色Technique:
   版本Version:
   做者Author:                   好笑癡狂
   日期Date:                      20120815
   備註Notes:
   解題分析:
        設f(m,n) 爲m個蘋果,n個盤子的放法數目,則先對n做討論,
        當n>m:一定有n-m個盤子永遠空着,去掉它們對擺放蘋果方法數目不產生影響。即if(n>m) f(m,n) = f(m,m)  
        當n<=m:不一樣的放法能夠分紅兩類:
        一、有至少一個盤子空着,即至關於f(m,n) = f(m,n-1);  
        二、全部盤子都有蘋果,至關於能夠從每一個盤子中拿掉一個蘋果,不影響不一樣放法的數目,即f(m,n) = f(m-n,n).
        而總的放蘋果的放法數目等於二者的和,即 f(m,n) =f(m,n-1)+f(m-n,n) 
    遞歸出口條件說明:
        當n=1時,全部蘋果都必須放在一個盤子裏,因此返回1;
        當沒有蘋果可放時,定義爲1种放法。由於: 遞歸的兩條路,第一條n會逐漸減小,終會到達出口n==1; 第二條m會逐漸減小,由於n>m時,咱們會return f(m,m) 因此終會到達出口m==0.
*/
#include<stdio.h>

int fun(int m,int n)  //m個蘋果放在n個盤子中共有幾種方法
{
    if(m==0||n==1)  //由於咱們老是讓m>=n來求解的,因此m-n>=0,因此讓m=0時候結束,若是改成m=1,
        return 1;    //則可能出現m-n=0的狀況從而不能獲得正確解    
    if(n>m)
        return fun(m,m);
    else
        return fun(m,n-1)+fun(m-n,n);
}

int main()
{
    int T,m,n;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&m,&n);
        printf("%d\n",fun(m,n));
    }
}

 若是將終止條件改成m==1||n==1,下面舉一個例子說明無限遞歸的造成過程:設計

 




==========================================================================
後來在其餘地方看到了一個與此題相同的但問題的情景稍微改變了一下:
把m個一樣的球放到n個一樣的袋子裏面,容許有的袋子空着不放,問共有多少種不一樣的放置方法?(用K表示)
例如:m=7,n=3,則k=8;這裏認爲(5,1,1)和(1,5,1)是同一种放置方法。
ps:差一點沒認出這個題的面目來……


另外一種思路:遞推或說動規吧
 1 #include <stdio.h>
 2 #include <string.h>
 3 #define maxM 11
 4 #define maxN 11
 5 int D[maxM][maxN]={0};
 6 int main(int argc, char *argv[])
 7 {
 8     int t,i,j,k,M,N,res;
 9     scanf("%d",&t);
10     for(k=0;k<t;k++)
11     {
12         scanf("%d%d",&M,&N);
13         memset(D,0,sizeof(D[0][0])*maxM*maxN);
14         //D[i][j]表示把i個果放進j個盤子 
15         for(j=1;j<=N;j++) D[1][j]=1;
16         for(i=1;i<=M;i++) D[i][1]=1;
17         for(i=2;i<=M;i++)
18         {
19             for(j=2;j<=N;j++)
20             {
21                 if(i<j) D[i][j]= D[i][i];
22                 else if(i>j) D[i][j]=D[i-j][j]+D[i][j-1];
23                 else D[i][j]=1+D[i][j-1]; // i==j的狀況 
24             }
25         }
26         printf("%d\n",D[M][N]);
27     }
28     return 0;
29 }

 

學生學習回溯算法時,設計的一個算法,挺有新意的:
by韋澤鴻:
 1 #include <iostream>
 2 using namespace std;
 3 int k=0,m,n;
 4 int ans[20]={0};
 5 void Print(){
 6     cout<<ans[0];
 7     for(int i=1;i<n;i++)
 8         cout<<' '<<ans[i];
 9     cout<<endl;
10 }
11 void dfs(int t,int x,int num){//第t個盤,已經用了x個,這一次至少要用num個 
12     if(t==n) {
13         if(x==m) {k++;/*Print();*/}
14         return;
15     }
16     for(int i=num;x+i<=m;i++){
17         ans[t]=i;
18         dfs(t+1,x+i,i);
19         ans[t]=0;
20     }
21 }
22 int main(){
23     int t;
24     cin>>t;
25     while(t--){
26         cin>>m>>n;
27         dfs(0,0,0);
28         cout<<k<<endl;
29         k=0;
30     }
31     return 0;
32 }
相關文章
相關標籤/搜索