Hanoi塔問題

請觀察上圖即可,圖片所顯示其實就是我們處理hanoi塔的三步。
(注意:圖片事網上找來的,漢諾塔問題是從 “A” 借助 “C” 轉移到 “B” )
假設f(x) : 把x個盤子 全部從A借助C轉移到B時 所用的步數。
以上圖舉例子。
1、首先先把4個盤子通過B轉移到C 操作步數為:f(4)
2、然后把最底層的盤子(編號為5)移動到B 操作步數為:1
3、最后把4個盤子通過A轉移到B 操作步數為:f(4)
通過上述例子:
可得到:f(5) = 2*f(4)+1
隨着盤子的增多,問題其實僅僅是從底層多加了delta層,但解決的步驟依舊一樣。
遞歸計算
1 #include<iostream> 2 using namespace std; 3 4 int Hanoi(int x){ 5 if( x == 1 ) 6 return 1 ; 7 else 8 return Hanoi(x-1) * 2 + 1 ; 9 } 10 int main() 11 { 12 int n ; 13 cin >> n ; 14 cout << Hanoi(n) << endl ; 15 return 0; 16 }
遞歸記錄路徑
1 #include<iostream> 2 using namespace std; 3 //打印其路徑 4 void Move( char u , char v ){ 5 printf("%c -> %c\n",u,v); 6 } 7 8 //起點為A , 過程中借助C , 最后到達B 9 //( A -> C -> B ) 10 void Hanoi( int n , char A , char B , char C ){ 11 if( n > 0 ){ 12 Hanoi( n - 1 , A , C , B ); 13 Move( A , B ); 14 Hanoi( n - 1 , C , B , A ); 15 } 16 } 17 int main() 18 { 19 int n ; 20 cin >> n ; 21 Hanoi(n,'A','B','C'); 22 return 0; 23 }
雖然代碼非常簡短,但是其遞歸過程比較復雜。
針對每一堆盤子來說,都是分三步,
通過 得知自己所在的位置,借助哪根柱子,最后要移動到哪一個柱子。
但是對於過程中的第一步,其實是遞歸后的結果。
Hanoi( n - 1 , A , C , B ); =>…… Move( A , B ); Hanoi( n - 1 , C , B , A );
由這句話往下到下一層
Hanoi( n - 1 , A , C , B ) { => …… (***) { Hanoi( n - 3 , A , C , B ) => { { Hanoi( n - 2 , A , B , C ) => { Move ( A , C ) { => { Move ( A , C ) { Hanoi( n - 3 , C , B , A ) { Hanoi( n - 2 , B , A , C )
一直往下遞歸,直到如上所示的.
"(***)" 作為第一句,然后回溯依次執行。
……
對於遞歸函數的設計,我們需要做到“整體把握”
但對於具體實現的過程,一定要明確其中的“具體過程”。
如果把該程序比做一棵遞歸樹(三叉樹),打印到屏幕的第一句執行的必定是整棵樹的最左下角的葉子結點。
對於問題的闡述:(以圖作為例子)
f(n) -> f(n-1) + 1 + f(n-1) f(n-1) -> f(n-2) + 1 + f(n-2) …… f(2) -> f(1) + "1" + f(1) f(0) + 1 + f(0) + "1" + f(0) + 1 + f(0) --- 第一個執行的操作
切面條
一根高筋拉面,中間切一刀,可以得到2根面條。
如果先對折1次,中間切一刀,可以得到3根面條。
如果連續對折2次,中間切一刀,可以得到5根面條。
那么,連續對折10次,中間切一刀,會得到多少面條呢?
題目提及到了“對折”一詞。 必定是會使面條加倍 f(0) = 2 f(1) = 3 f(2) = 5 f(3) = 9 f(4) = 17 …… 肯定是圍繞着冪次進行找規律。 所以答案就是f(n) = 2^n + 1
補充的內容,並不是考點。
快速冪
算法步驟如上所示:
其實僅僅是利用任何數字轉變成2進制后,
每一個位置上權值要么為‘1’,要么為‘0’的特點。
我們可以通過基數和指數相互配合,基數 和底數 同時進行左移。
若當前指數所對應的位置是'1'
ans 必須乘以當前底數
若當前指數所對應的位置為'0'
不執行任何操作
若指數所對應的位置越界時則算法結束,當前對應的ans=pow( base , n )
1 #include<iostream> 2 using namespace std; 3 4 //快速冪函數 5 int qpow( int a , int b ){ 6 int ans = 1 ; 7 //把目標次冪b ->轉化成2進制. 8 while( b ){ 9 //如果當前是b最末尾為1答案進行累成 10 if( b % 2 == 1 ){ 11 ans = ans * a ; 12 } 13 //基數和次冪同時移位 14 b = b / 2 ; 15 a = a * a ; 16 } 17 //輸出答案 18 return ans ; 19 } 20 int main() 21 { 22 int n = 10 , Base = 2; 23 cout << qpow( Base , n ) + 1 << endl ; 24 return 0; 25 }
平面上的直線
【題意】
平面上有 n 條直線,最多可以把整個平面分成多少份?

L(0) = 1
L(1) = 2
L(2) = 4
L(3) = 7
通過觀察可以得知:
L(n) = L(n-1) + n
= L(n-2) + n-1 + n
= ……
= 1 + Sn

排列問題

給定 n 個元素,求出這 n 個元素的全排列。
1 //排列問題 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 const int N = 20 ; 6 int a[N] ; 7 void Perm( int S , int E ){ 8 if( S == E ){ 9 for( int i = 1 ; i <= E ; i++ ){ 10 printf("%3d",a[i]); 11 } 12 putchar('\n'); 13 return ; 14 } 15 for( int i = S ; i <= E ; i++ ){ 16 swap( a[S] , a[i] ); 17 Perm( S + 1 , E ); 18 swap( a[S] , a[i] ); 19 } 20 } 21 int main() 22 { 23 int n = 3 ; 24 for( int i = 1 ; i <=n ; i++ ) a[i] = i ; 25 Perm( 1 , n ); 26 return 0 ; 27 }
整數划分問題
【題目描述】
將正整數 n 划分為一系列正整數的和:
6=6 6=5+1 6=4+2=4+1+1 6=3+3=3+2+1=3+1+1+1 6=2+2+2=2+2+1+1=2+1+1+1+1 6=1+1+1+1+1+1+1
共11 種情況
書本P14頁有具體的推導過程及解釋
1 //整數划分問題 - 公式法 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 int solve( int n , int m ){ 6 if( ( n < 1 ) || ( m < 1 ) ) 7 return 0 ; 8 else if( ( n==1) || ( m==1) ) 9 return 1 ; 10 else if( n < m ) 11 return solve( n , n ); 12 else if( n == m ) 13 return solve( n , m-1 ) + 1 ; 14 else 15 return solve( n , m - 1 ) + solve( n - m , m ); 16 } 17 int main() 18 { 19 printf("%d\n",solve(6,6)); 20 return 0 ; 21 }
模擬多項式乘法即可。
具體原理:https://blog.csdn.net/Z_sea/article/details/86529635
1 //整數划分-母函數 2 #include<cstdio> 3 using namespace std; 4 const int N = 1e3 + 10 ; 5 int f[N] , tmp[N] ; 6 int main() 7 { 8 int n = 6 ; 9 f[0] = 1 ; 10 for( int i = 1 ; i <= n ; i ++ ){ 11 for( int j = 0 ; j <= n ; j ++ ){ 12 for( int k = i ; k <= n ; k += i ){ 13 tmp[j+k] += f[j] ; 14 } 15 } 16 for( int j = 0 ; j <= n ; j++ ){ 17 f[j] += tmp[j] ; 18 tmp[j] = 0 ; 19 } 20 } 21 printf("%d\n",f[n]); 22 return 0 ; 23 }
二分搜索技術

【題目描述】
給定已排好序的 n 個元素 a[0:n-1],在其中查找一個特定元素 x。
1 #include<cstdio> 2 using namespace std; 3 const int N = 1e3 ; 4 int a[N] = { 1 , 2 , 3 , 6 , 7 , 11 }; 5 int main() 6 { 7 int x ; 8 bool flag = false ; 9 scanf("%d",&x); 10 int L = 0 , R = 6 ; 11 while( L <= R ){ 12 int Mid = ( L + R ) / 2 ; 13 if( a[Mid] == x ){ 14 printf("Index : %d\n",Mid); 15 flag = true ; 16 } 17 18 if( x < a[Mid] ) R = Mid - 1 ; 19 else L = Mid + 1 ; 20 } 21 if( !flag ){ 22 printf("Not Found\n"); 23 } 24 return 0 ; 25 } 26 /* 27 1 28 Index : 0 29 30 10 31 Not Found 32 */
棋盤覆蓋


【分析】
分四個部分,如果是特殊點標注的部分,則另外3個部分的頂角位置標注,再進行分治。
最后問題會一直標記,問題規模不斷減少,直到為一個格子時則返回。
【具體代碼】
1 //棋盤覆蓋 2 #include<cstdio> 3 const int N = 8 ; 4 //t(x,y) -> top-left :左上角的坐標 5 //s(x,y) -> special :特殊標識的坐標 6 //L : 討論當前方格的邊長 7 //tag : 時間戳 8 int chessboard[N][N] ; 9 int tag = 1 ; 10 void f( int tx , int ty , int sx , int sy , int L ){ 11 12 if( L == 1 ){ 13 return ; 14 } 15 int t = tag ++ ; 16 int len = L / 2 ; 17 18 //判斷特殊點是否在左上部分? 19 if( sx < tx + len && sy < ty + len ){ 20 //繼續分治 21 f( tx , ty , sx , sy , len ); 22 }else{ 23 //填上序號,並繼續分治 24 chessboard[tx+len-1][ty+len-1] = t ; 25 f( tx , ty , tx+len-1 , ty+len-1 , len ); 26 } 27 28 //判斷特殊點是否在右上部分? 29 if( sx < tx + len && ty + len <= sy ){ 30 //繼續分治 31 f( tx , ty + len , sx , sy , len ); 32 }else{ 33 //填上序號,並繼續分治 34 chessboard[tx+len-1][ty+len] = t ; 35 f( tx , ty + len , tx+len-1, ty+len , len ); 36 } 37 38 //判斷特殊點是否在左下部分? 39 if( tx + len <= sx && sy < ty + len ){ 40 //繼續分治 41 f( tx + len , ty , sx , sy , len ); 42 }else{ 43 //填上序號,並繼續分治 44 chessboard[tx+len][ty+len-1] = t ; 45 f( tx + len , ty , tx+len , ty+len-1 , len ); 46 } 47 48 //判斷特殊點是否在右下部分? 49 if( tx + len <= sx && ty + len <= sy ){ 50 //繼續分治 51 f( tx + len , ty + len , sx , sy , len ); 52 }else{ 53 //填上序號,並繼續分治 54 chessboard[tx+len][ty+len] = t ; 55 f( tx + len , ty + len , tx+len , ty+len , len ); 56 } 57 58 59 } 60 int main() 61 { 62 //棋盤初始左上角,初始特殊點,邊長為8 63 f( 0 , 0 , 0 , 1 , 8 ); 64 for( int i = 0 ; i < 8 ; i++ ){ 65 for( int j = 0 ; j < 8 ; j++ ){ 66 printf("%4d",chessboard[i][j]); 67 } 68 putchar('\n'); 69 } 70 return 0 ; 71 }
