回溯(backtracking):
有“通用解題法”之稱。用它可以系統地搜索問題的所有解。它在問題的解空間樹中,按深度優先策略,從根結點出發搜索解空間樹。算法搜索至解空間樹的任一結點時,先判斷該結點是否包含問題的解,如果肯定不包含,則跳過對以該節點為根的子樹的搜索,逐層向其祖先結點回溯;否則,進入該子樹,繼續按深度優先策略繼續搜索。
八皇后問題:
問題描述:在8×8的國際象棋棋盤上放置八個皇后,使得任何一個皇后都無法直接吃掉其他的皇后?為了達到此目的,任兩個皇后都不能處于同一條橫行、縱行或斜線上。
#include <iostream>
#include <cmath>
#define N 8
using namespace std;
int a[8];//皇后每行每列有且只能有一個,所以用a[i]代表第i個皇后(在第i行)放在第a[i]列
int cnt=1;//記錄解的序號
void search(int m);
int canPlace(int row ,int col);
void printResult();
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char** argv) {
search(0);
return 0;
}
void search(int m)
{
if(m>=N)printResult();
else
{
int i;
for(i=0;i<N;i++)//枚舉8列
{
if(canPlace(m,i))
{
a[m]=i;
search(m+1);
}
a[m]=0;
}
}
}
int canPlace(int row ,int col)
{
int i;
for(i=0;i<row;i++) //皇后肯定不會同行,a[i]==col檢查是否同列,abs(i-row)==abs(a[i]-col)檢查是否同對角線,如果同對角線,行差和列差的絕對值應該相等。
{
if(a[i]==col||abs(i-row)==abs(a[i]-col))return 0;
}
return 1;
}
void printResult()
{
int i,j;
cout<<"No "<<cnt<<":"<<endl;
for(i=0;i<N;++i)
{
for(j=0;j<a[i];++j)
{
cout<<".";
}
cout<<"A";
j++;
for(;j<N;++j)
{
cout<<".";
}
cout<<endl;
}
cnt++;
}
下面給出4皇后回溯過程:
以及八皇后回溯過程(樹太深,所以把時間設置的很快,大家看個熱鬧就行,感受一下計算機的力量):
0-1背包問題:
問題描述:在0 / 1背包問題中,需對容量為c 的背包進行裝載。從n 個物品中選取裝入背包的物品,每件物品i 的重量為wi ,價值為pi 。對于可行的背包裝載,背包中物品的總重量不能超過背包的容量,最佳裝載是指所裝入的物品價值最高。
#include <iostream>
#define MAX 10
using namespace std;
int n;//物品個數
int c;//背包容量
int maxValue;//記錄所選物品最大價值
int w[MAX];//物品重量數組
int v[MAX];//物品價值數組
int a[MAX];//a數組存放當前物品的選擇情況
//a[i]=0表述不選第i個物品,a[i]=1表示選擇第i個物品
int result[100];
int cnt;
void init();
void readData();
void search(int m);
void checkMax();
void printResult();
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char** argv) {
cin>>n>>c;
while(n!=0||c!=0)
{
init();
readData();
search(0);
result[cnt]=maxValue;
cnt++;
cin>>n>>c;
}
printResult();
return 0;
}
void init()
{
maxValue=0;
int i;
for(i=0;i<MAX;++i)
{
w[i]=v[i]=a[i]=0;
}
}
void readData()
{
int i;
for(i=0;i<n;i++)
{
cin>>w[i];
}
for(i=0;i<n;i++)
{
cin>>v[i];
}
}
void search(int m)
{
if(m>=n)
checkMax();
else
{
a[m]=0;//不要該物品
search(m+1);
a[m]=1;//要該物品
search(m+1);
}
}
void checkMax()
{
int weight=0,value=0;
int i;
for(i=0;i<n;i++)
{
if(a[i]==1)
{
weight+=w[i];
value+=v[i];
}
if(weight<=c)
{
if(value>maxValue)
maxValue=value;
}
}
}
void printResult()
{
int i;
for(i=0;i<cnt;++i)
{
cout<<result[i]<<endl;
}
}
堡壘問題:
如圖城堡是一個4×4的方格,為了保衛城堡,現需要在某些格子里修建一些堡壘。城堡中的某些格子是墻,其余格子都是空格,堡壘只能建在空格里,每個堡壘都可以向上下左右四個方向射擊,如果兩個堡壘在同一行或同一列,且中間沒有墻相隔,則兩個堡壘都會把對方打掉。問對于給定的一種狀態,最多能夠修建幾個堡壘。
#include <iostream>
#define MAX 5
int n;
int a[MAX*MAX];
int maxnum;
using namespace std;
void search(int m);
int canPlace(int m);
void checkMax();
/*run this program using the console pauser or add your own getch, system("pause") or input loop*/
int main(int argc, char** argv) {
while(cin>>n&&n!=0)
{
maxnum=0;
char c;
int i;
for(i=0;i<n*n;++i)
{
cin>>c;
if(c=='.')a[i]=1;
else if(c=='X')a[i]=0;
}
search(0);
cout<<maxnum<<endl;
}
return 0;
}
void search(int m)
{
if(m>=n*n)checkMax();
else
{
search(m+1);
if(canPlace(m)==1)
{
a[m]=2;//放堡壘
search(m+1);
a[m]=1;//把堡壘拿掉
}
}
}
int canPlace(int m)
{
if(a[m]==0)return 0;
int row,col;
row=m/n;
col=m%n;
int i,j;
for(i=0;i<m;++i)
{
if(a[i]==2)
{
if(i/n==row)//同行有堡壘
{
int flag=0;
for(j=i+1;j<m;++j)
{
if(a[j]==0)flag=1;
}
if(flag==0)return 0;
}
if(i%n==col)//同列有堡壘
{
int flag=0;
for(j=col;j<m;j+=n)
{
if(a[j]==0)flag=1;
}
if(flag==0)return 0;
}
}
}
return 1;
}
void checkMax()
{
int i;
int cnt=0;
for(i=0;i<n*n;++i)
{
if(a[i]==2)cnt++;
}
if(cnt>maxnum)maxnum=cnt;
}
素數環問題:
問題描述:把從1到20這20個數擺成一個環,要求相鄰的兩個數的和是一個素數。
#include <stdio.h>
#include <cmath>
void search(int);
void init();
void printresult();
int isprime(int);
void swap(int,int);
int a[21];
int main()
{
init();
search(2);
}
int isprime(int num)
{
int i,k;
k=sqrt(num);
for(i=2;i<=k;i++)
if(num%i==0)
return(0);
return(1);
}
void printresult()
{
int i;
for(i=1;i<=20;i++)
printf("%3d",a[i]);
printf("\n");
}
void search(int m)
{
int i;
if(m>20)
{
if(isprime(a[1]+a[20])) //判斷首尾是否滿足要求
printresult();
return;
}
else
{
for(i=m;i<=20;i++) //注意i是從m開始的而不是m+1
{
swap(m,i);
if(isprime(a[m-1]+a[m]))
search(m+1);
swap(m,i);
}
}
}
void swap(int m, int i)
{
int t;
t=a[m];
a[m]=a[i];
a[i]=t;
}
void init()
{
int i;
for(i=0;i<21;i++)
a[i]=i;
}
迷宮問題:
問題描述:給一個20×20的迷宮、起點坐標和終點坐標,問從起點是否能到達終點。
輸入:多個測例。輸入的第一行是一個整數n,表示測例的個數。接下來是n個測例,每個測例占21行,第一行四個整數x1,y1,x2,y2是起止點的位置(坐標從零開始),(x1,y1)是起點,(x2,y2)是終點。下面20行每行20個字符,’.’表示空格;’X’表示墻。
輸出:每個測例的輸出占一行,輸出Yes或No。
#include <iostream>
using namespace std;
#define N 20//20*20的迷宮
int k=20,t=20;//行數和列數
int a[N][N];//1代表墻,0代表可走,2代表已經走過
int n;//測例個數
int startRow,startCol;//入口坐標
int endRow,endCol;//出口坐標
int flag=0;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
void readData();
void search(int row,int col);
int canPlace(int row,int col);
void printResult();
int main(int argc, char** argv) {
cin>>n;
while(n--)
{
flag=0;
readData();
search(startRow,startCol);
printResult();
}
return 0;
}
void readData()
{
cin>>startRow>>startCol;
cin>>endRow>>endCol;
int i,j;
char c;
for(i=0;i<k;i++)
{
for(j=0;j<t;j++)
{
cin>>c;
if(c=='.')a[i][j]=0;
else if(c=='X')a[i][j]=1;
}
}
}
void search(int row,int col)
{
if((row==endRow)&&(col==endCol))
{
flag=1;
return;
}
else
{
int r,c;
a[row][col]=2;//標記已經走過的位置
r=row,c=col-1;//向左走
if(canPlace(r,c))search(r,c);
r=row,c=col+1;//向右走
if(canPlace(r,c))search(r,c);
r=row-1,c=col;//向上走
if(canPlace(r,c))search(r,c);
r=row+1,c=col;//向下走
if(canPlace(r,c))search(r,c);
}
}
int canPlace(int row,int col)//判斷是否可走
{
if(row>=0&&row<k&&col>=0&&col<t&&a[row][col]==0)return 1;//不越界且可以走
else
return 0;
}
void printResult()
{
if(flag==1)cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
農場灌溉問題:
問題描述:一農場由圖所示的十一種小方塊組成,藍色線條為灌溉渠。若相鄰兩塊的灌溉渠相連則只需一口水井灌溉。給出若干由字母表示的最大不超過50×50具體由(m,n)表示,的農場圖,編程求出最小需要打的井數。每個測例的輸出占一行。當M=N=-1時結束程序。

#include <iostream>
using namespace std;
int m, n;
struct Maze
{
char ch; //方塊類型
int visited; //訪問標記
int left, right, up, down; //連通線
}maze[100][100];
int dir[4][2] = {{1,0}, {-1,0}, {0,-1}, {0,1}}; //上下左右四個方向
bool Place(int x, int y, int nx, int ny)
{
//未越界且未被訪問過
if(nx>0 && nx<=m && ny>0 && ny<=n && !maze[nx][ny].visited)
{
//向左
if(nx==x && ny==y-1 && maze[x][y].left && maze[nx][ny].right)
return true;
//向下
if(nx==x+1 && ny==y && maze[x][y].down && maze[nx][ny].up)
return true;
//向右
if(nx==x && ny==y+1 && maze[x][y].right && maze[nx][ny].left)
return true;
//向上
if(nx==x-1 && ny==y && maze[x][y].up && maze[nx][ny].down)
return true;
}
return 0;
}
void dfs(int x, int y)
{
maze[x][y].visited = 1;
for(int i = 0; i < 4; i++)
{
int nx = x+dir[i][0];
int ny = y+dir[i][1];
//將所有可聯通的區域全部標記
if(Place(x, y, nx, ny))
dfs(nx, ny);
}
}
int main()
{
while(cin >> m >> n && (m!=-1 || n!=-1))
{
int cnt = 0;
for(int i = 1; i <= m; i++)
for(int j = 1; j <= n; j++)
{
cin >> maze[i][j].ch;
//初始化
maze[i][j].visited = 0;
maze[i][j].down = 0;
maze[i][j].up = 0;
maze[i][j].right = 0;
maze[i][j].left = 0;
switch(maze[i][j].ch)
{
case'A': maze[i][j].left=1;
maze[i][j].up=1;
break;
case'B': maze[i][j].right=1;
maze[i][j].up=1;
break;
case'C': maze[i][j].left=1;
maze[i][j].down=1;
break;
case'D': maze[i][j].right=1;
maze[i][j].down=1;
break;
case'E': maze[i][j].up=1;
maze[i][j].down=1;
break;
case'F': maze[i][j].left=1;
maze[i][j].right=1;
break;
case'G': maze[i][j].left=1;
maze[i][j].right=1;
maze[i][j].up=1;
break;
case'H': maze[i][j].left=1;
maze[i][j].up=1;
maze[i][j].down=1;
break;
case'I': maze[i][j].left=1;
maze[i][j].right=1;
maze[i][j].down=1;
break;
case'J': maze[i][j].right=1;
maze[i][j].up=1;
maze[i][j].down=1;
break;
case'K': maze[i][j].left=1;
maze[i][j].right=1;
maze[i][j].up=1;
maze[i][j].down=1;
break;
}
}
//連通區域個數
for(int i = 1; i <= m; i++)
for(int j = 1; j <= n; j++)
{
if(maze[i][j].visited == 0) // 未被訪問過
{
dfs(i, j);
cnt ++;
}
}
cout << cnt << endl;
}
return 0;
}