非遞迴棧式回溯與遞迴法的幾個問題
阿新 • • 發佈:2018-11-01
1.迷宮求解
2.八皇后
非遞迴
#include <stdio.h> #include <math.h> #include <malloc.h> void nQueens(int *x, int n); /*求解n皇后問題*/ int place(int *x, int k); /*判斷是否可以在第k行第x[k]列擺放皇后*/ void printSolution(int *x, int n); /*輸出求解結果*/ int main() { int n; int *x; /*存放求解結果的陣列首地址*/ scanf("%d", &n); x=(int*)malloc(sizeof(int)*(n+1)); /*動態分配陣列空間, x[0]空閒*/ nQueens(x, n); return 0; } /*如果一個皇后能放在第k行第x[k]列,則返回真(1),否則返回假(0)*/ int place(int *x, int k) { int i; /*對前k-1行,逐行考察*/ for(i=1; i<k; i++) { /*如果前k-1行中有某行的皇后與第k行的在同一列或同一斜線,返回0*/ if((x[i]==x[k])||(fabs(x[i]-x[k])==fabs(i-k))) return 0; } /*能執行下一句,說明在第k行第x[k]列擺放皇后,不會互相攻擊*/ return 1; } /*求解在n×n的棋盤上,放置n個皇后,使其不能互相攻擊*/ void nQueens(int *x, int n) { int k; k = 1; /*k是當前行*/ x[k] = 0; /*x[k]是當前列,進到迴圈中,立刻就會執行x[k]++,而選擇了第1列*/ while(k>0)/*當將所有可能的解嘗試完後,k將變為0,結束求解過程*/ { x[k]++; /*移到下一列*/ while(x[k]<=n && !place(x, k)) /*逐列考察,找出能擺放皇后的列x[k]*/ x[k]++; if(x[k]<=n) /*找到一個位置可以擺放皇后*/ { if(k==n) /*是一個完整的解,輸出解*/ printSolution(x, n); else /*沒有完成最後一行的選擇,是部分解,轉向下一行*/ { k++; /*接著考察下一行*/ x[k]=0; /*到迴圈開始執行x[k]++後,下一行將從第1列開始考察*/ } } else /*對應x[k]>n的情形,這一行已經沒有再試的必要,回溯到上一行*/ k--; /*上一行在原第x[k]列的下1列開始考察*/ } } /*輸出求解結果*/ void printSolution(int *x, int n) { int i, j; for (i = 1; i <= n; i++) /*輸出第i行*/ { for (j=1; j<=n; j++) { if (j == x[i]) /*第x[i]列輸出Q,其他列輸出*號 */ printf("Q"); else printf("*"); } printf("\n"); } printf("\n"); }
3.矩形對角點最短路徑
問題描述,每次只能向上下左右四個方向移動一步,求從點(x,y)到(m,n)最短步數的走法。
分析:從題意可知要使步數最少,則必須是往右或者往上走,且最大步數肯定為(m-x)+ (n-y);如此則有兩種解法,遞迴的樹深度搜索,非遞迴的棧式回溯。下面列出程式碼。
方法一:遞迴求解。
int num = 0; void Search(int x, int y, int m, int n){ if(x==m||y==n){//不走回頭路,所以只要或就行 num++; return; } search(x+1,y,m,n);//右走 search(x,y+1,m,n);//上週 }
方法二:棧式回溯
int search(int x, int y, int m, int n){//回溯法 //if(m == n == 1) // return 1; int f[m+n-1] = {0}; int sum = 0; int k = 1; while(k>0){ if(x == m -1){ sum++; k--; x--; continue; } else if(y == n - 1){ sum++; k--; y--; continue; } if(f[k] == 0){ f[k]++; k++; x++; } else if(f[k] == 1){ f[k]++; k++; y++; //continue; } else if(f[k] == 2){ f[k] = 0;//清0,方便再次進入 if(f[k-1] == 2)//判斷回溯方向 y--; else x--; k--; } } return sum; }
動態規劃法:由遞迴式可知狀態轉移方程為s[i][j] = s[i-1][j] + s[i][j],i,j>0.程式碼如下:
int s[101][101] = {0};
int uniquePaths(int m, int n) {//遞迴法
//return search(0, 0, m, n);
//vector<int> temp(n,0);
// vector<vector<int>> vt_rt(m,temp);
int max = m>n?m:n;
for(int i = 1; i <= max; i++)//初始化
{
s[1][i] = 1;
s[i][1] = 1;
}
for(int i = 2; i <= m; i++)
for(int j = 2; j <= n; j++)
s[i][j] = s[i-1][j] + s[i][j-1];
return s[m][n];
}