什么是回溯算法
回溯算法實(shí)際上一個(gè)類似枚舉的搜索嘗試過程,主要是在搜索嘗試過程中尋找問題的解,當(dāng)發(fā)現(xiàn)已不滿足求解條件時(shí),就 “回溯” 返回,嘗試別的路徑。
回溯法是一種選優(yōu)搜索法,按選優(yōu)條件向前搜索,以達(dá)到目標(biāo)。但當(dāng)探索到某一步時(shí),發(fā)現(xiàn)原先選擇并不優(yōu)或達(dá)不到目標(biāo),就退回一步重新選擇,這種走不通就退回再走的技術(shù)為回溯法,而滿足回溯條件的某個(gè)狀態(tài)的點(diǎn)稱為 “回溯點(diǎn)”。許多復(fù)雜的,規(guī)模較大的問題都可以使用回溯法,有“通用解題方法”的美稱。
回溯算法的基本思想是:從一條路往前走,能進(jìn)則進(jìn),不能進(jìn)則退回來,換一條路再試。
八皇后問題
我們學(xué)習(xí)了什么是回溯算法,聽起來很簡單,但具體怎么使用呢?下面我們通過一個(gè)例子來說明回溯算法的用法。
八皇后問題:有一個(gè)8X8的棋盤,希望往里放8個(gè)棋子(皇后),每個(gè)棋子所在的行、列、對角線都不能有另一個(gè)棋子,找到所有滿足這種要求的放棋方式。
解題思路:把這個(gè)問題劃分成8個(gè)階段,依次將8個(gè)棋子放到第一行、第二行、第三行....第八行。在放置的過程中,不停地檢查當(dāng)前的方法,是否滿足要求。如果滿足,則跳到下一行繼續(xù)放置棋子;如果不滿足,那就換一種方法,繼續(xù)嘗試。
代碼實(shí)現(xiàn)如下:
public class Solution {
int[] result = new int[8]; // 全局或成員變量,下標(biāo)表示行,值表示queen存儲在哪一列
public void Cal8Queens (int row) { // 調(diào)用方式:Cal8Queens(0);
if (row == 8) { // 8個(gè)棋子都放置好了,打印結(jié)果
PrintQueens (result);
return; // 8行棋子都放好了,已經(jīng)沒法再往下遞歸了,所以就return
}
for (int column = 0; column < 8; ++column) { // 每一行都有8中放法
if (IsOK (row, column)) { // 有些放法不滿足要求
result[row] = column; // 第row行的棋子放到了Column列
Cal8Queens (row + 1); // 考察下一行
}
}
}
private bool IsOK (int row, int column) { // 判斷row行column列放置是否合適
int leftup = column - 1, rightup = column + 1;
for (int i = row - 1; i >= 0; --i) { // 逐行往上考察每一行
if (result[i] == column) return false; // 第i行的column列有棋子嗎?
if (leftup >= 0) { // 考察左上對角線:第i行l(wèi)eftup列有棋子嗎?
if (result[i] == leftup) return false;
}
if (rightup < 8) { // 考察右上對角線:第i行rightup列有棋子嗎?
if (result[i] == rightup) return false;
}
--leftup;
++rightup;
}
return true;
}
private void PrintQueens (int[] result) { // 打印出一個(gè)二維矩陣
for (int row = 0; row < 8; ++row) {
for (int column = 0; column < 8; ++column) {
if (result[row] == column) Console.Write ("Q ");
else Console.Write ("* ");
}
Console.WriteLine ();
}
Console.WriteLine();
}
}
總結(jié)
回溯算法的思想非常簡單,大部分情況下,都是用來解決廣義的搜索問題:從一組可能的解中,選擇出一個(gè)滿足要求的解。
回溯算法非常適合用遞歸來實(shí)現(xiàn),在實(shí)現(xiàn)的過程中,剪枝操作是提高回溯效率的一種技巧。利用剪枝,并不需要窮舉搜索所有的情況,從而提高搜索效率。