標題:李白打酒
話說大詩人李白,一生好飲。幸好他從不開車。
一天,他提着酒壺,從家里出來,酒壺中有酒2斗。他邊走邊唱:
無事街上走,提壺去打酒。
逢店加一倍,遇花喝一斗。
這一路上,他一共遇到店5次,遇到花10次,已知最后一次遇到的是花,他正好把酒喝光了。
請你計算李白遇到店和花的次序,可以把遇店記為a,遇花記為b。則:babaabbabbabbbb 就是合理的次序。像這樣的答案一共有多少呢?請你計算出所有可能方案的個數(包含題目給出的)。
注意:通過瀏覽器提交答案。答案是個整數。不要書寫任何多余的內容。
答案:14
先提供兩種用枚舉方式來解決此問題的方法,前面14個格子要擺放的是0,1(用0標識花,1標識店)
方案一,用0,1把前14個格子按照字典序打印出來,但要保證其0,1的總數小於規定總數。然后模擬這個過程。
代碼:
 
          
         1 /*方法1*/#include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define MAXN 20 5 using namespace std; 6 int num[MAXN],s=0;//花記為 0,店記為 1 7 void dfs(int cur) 8 { 9 int n0=0,n1=0,i,j,c=2; 10 bool flag=true; 11 if(cur==15) 12 { 13 for(i=1;i<=14;i++) 14 { 15 c=num[i]==0?c-1:c*2; 16 if(c==0) 17 { 18 flag=false; 19 break; 20 } 21 } 22 if(flag==false) 23 return ; 24 else 25 { 26 if(c==1) 27 { 28 s++; 29 for(i=1;i<=14;i++) 30 cout<<num[i]<<' '; 31 cout<<0<<endl; 32 } 33 return ; 34 } 35 } 36 for(i=0;i<=1;i++) 37 { 38 if(i==0) 39 { 40 for(j=1;j<=cur-1;j++) 41 { 42 if(num[j]==0) 43 n0++; 44 } 45 if(n0<9) 46 { 47 num[cur]=0; 48 dfs(cur+1); 49 num[cur]=-1; 50 } 51 } 52 else 53 { 54 for(j=1;j<=cur-1;j++) 55 { 56 if(num[j]==1) 57 n1++; 58 } 59 if(n1<5) 60 { 61 num[cur]=1; 62 dfs(cur+1); 63 num[cur]=-1; 64 } 65 66 } 67 } 68 } 69 int main() 70 { 71 memset(num,-1,sizeof(num)); 72 dfs(1); 73 cout<<s<<endl; 74 return 0; 75 }
方案二,我們其實只要將5個1填入前14個格子里,其實是
 種方案,我們依舊可以用枚舉的方式實現,依次去按字典序排列5個數(但必須是按遞增的序列排列)選取的數字是從1-14里選擇。
代碼:
 
          
         1 /*方法2*/ 2 #include<iostream> 3 #include<cstdio> 4 #include<cstring> 5 #define MAXN 20 6 using namespace std; 7 int num[MAXN],ans[MAXN],s=0;//花記為 0,店記為 1 8 void dfs(int cur,int k) 9 { 10 int ok,c=2,i,j; 11 bool flag=true; 12 if(cur==6) 13 { 14 memset(num,0,sizeof(num)); 15 for(i=1;i<=5;i++) 16 { 17 num[ans[i]]=1; 18 } 19 for(i=1;i<=14;i++) 20 { 21 c=num[i]==0?c-1:c*2; 22 if(c==0) 23 { 24 flag=false; 25 break; 26 } 27 } 28 if(flag==false) 29 return ; 30 else 31 { 32 if(c==1) 33 { 34 s++; 35 for(i=1;i<=14;i++) 36 cout<<num[i]<<' '; 37 cout<<0<<endl; 38 } 39 return ; 40 } 41 } 42 for(i=k+1;i<=14;i++) 43 { 44 ok=1; 45 for(j=1;j<=cur-1;j++) 46 { 47 if(ans[j]==i) 48 ok=0; 49 } 50 if(ok) 51 { 52 ans[cur]=i; 53 dfs(cur+1,i); 54 } 55 } 56 } 57 int main() 58 { 59 memset(ans,0,sizeof(ans)); 60 dfs(1,0); 61 cout<<s<<endl; 62 return 0; 63 }
而遞歸的方式實現將變得更加簡潔。
由於實現此過程的總數等於開頭為a和開頭為b的總和,再遞歸這兩個決策,直到a==0&&b==0&&c==1結束。
代碼:
 
          
         1 /*遞歸*/ 2 #include<iostream> 3 using namespace std; 4 int sum=0; 5 int f(int a,int b,int c){ // a:店的總數 b:花的總數減1 c:酒的初值 6 // 任何初始狀況,都有兩個可能:先遇到店,或者先遇到花 7 if(a>0) 8 f(a-1,b,c*2); // 逢店加一倍 9 if(b>0) 10 f(a,b-1,c-1); // 遇花喝一斗 11 if(a==0&&b==0&&c==1) //這個是滿足要求的終止條件。沒有店剩下,還剩一朵花和一斗酒 12 sum=sum+1; 13 return sum; 14 } 15 int main() 16 { 17 f(5,9,2); 18 cout<<sum<<endl; 19 return 0; 20 }
