算法詳解之回溯法具體實(shí)現(xiàn)
理論輔助:
回溯算法也叫試探法,它是一種系統(tǒng)地搜索問題的解的方法?;厮菟惴ǖ幕舅枷胧牵簭囊粭l路往前走,能進(jìn)則進(jìn),不能進(jìn)則退回來,換一條路再試。用回溯算法解決問題的一般步驟為:
1、定義一個(gè)解空間,它包含問題的解。
2、利用適于搜索的方法組織解空間。
3、利用深度優(yōu)先法搜索解空間。
4、利用限界函數(shù)避免移動(dòng)到不可能產(chǎn)生解的子空間。
問題的解空間通常是在搜索問題的解的過程中動(dòng)態(tài)產(chǎn)生的,這是回溯算法的一個(gè)重要特性。
還是那個(gè)基調(diào),不喜歡純理論的東西,喜歡使用例子來講訴理論,在算法系列總結(jié):動(dòng)態(tài)規(guī)劃(解公司外包成本問題) 的那一節(jié)里面 我們舉得是經(jīng)典的0-1背包問題,在回溯算法里面也有一些很經(jīng)典的問題,當(dāng)然,動(dòng)態(tài)規(guī)劃的0-1背包問題其實(shí)也可以使用回溯算法來解。在諸如此類似的求最優(yōu)解的問題中,大部分其實(shí)都可以用回溯法來解決,可以認(rèn)為回溯算法一個(gè)”通用解題法“,這是由他試探性的行為決定的,就好比求一個(gè)最優(yōu)解,我可能沒有很好的概念知道怎么做會(huì)更快的求出這個(gè)最優(yōu)解,但是我可以嘗試所有的方法,先試探性的嘗試每一個(gè)組合,看看到底通不通,如果不通,則折回去,由最近的一個(gè)節(jié)點(diǎn)繼續(xù)向前嘗試其他的組合,如此反復(fù)。這樣所有解都出來了,在做一下比較,能求不出最優(yōu)解嗎?
例子先行,現(xiàn)在我們來看看經(jīng)典的N后問題
問題描述:在n*n格的棋盤上放置彼此不受攻擊的n個(gè)皇后。按照國(guó)際象棋的規(guī)矩,皇后可以攻擊與之處在同一行或同一列或同一斜線上的棋子。n后問題等價(jià)于在n*n格的棋盤上方置n個(gè)皇后,任何2個(gè)皇后不放在同一行或同一列或同一斜線上。我們需要求的是可放置的總數(shù)。
基本思路: 用n元組x[1;n]表示n后問題的解。其中,x[i]表示皇后i放置在棋盤的第i行的第x[i]列。由于不容許將2個(gè)皇后放在同一列上,所以解向量中的x[i]互不相同。2個(gè)皇后不能放在同一斜線上是問題的隱約束。對(duì)于一般的n后問題,這一隱約束條件可以化成顯約束的形式。如果將n*n 格的棋盤看做二維方陣,其行號(hào)從上到下,列號(hào)從左到右依次編號(hào)為1,2,...n。從棋盤左上角到右下角的主對(duì)角線及其平行線(即斜率為-1的各斜線)上,2個(gè)下標(biāo)值的差(行號(hào)-列號(hào))值相等。同理,斜率為+1的每條斜線上,2個(gè)下標(biāo)值的和(行號(hào)+列號(hào))值相等。因此,若2個(gè)皇后放置的位置分別是(i,j)和(k,l),且 i-j = k -l 或 i+j = k+l,則說明這2個(gè)皇后處于同一斜線上。以上2個(gè)方程分別等價(jià)于i-k = j-l 和 i-k =l-j。由此可知,只要|i-k|=|l-j|成立,就表明2個(gè)皇后位于同一條斜線上。
1、從空棋盤起,逐行放置棋子。
2、每在一個(gè)布局中放下一個(gè)棋子,即推演到一個(gè)新的布局。
3、如果當(dāng)前行上沒有可合法放置棋子的位置,則回溯到上一行,重新布放上一行的棋子。
代碼:
#include <stdio.h>
#include <math.h>
#include<stdlib.h>
static int n,x[1000];
static long sum;
int Place(int k)
{
for(int j=1;j <k; j++)
if((abs(k-j) == abs(x[j]-x[k]))||(x[j]==x[k])) return 0;
return 1;
}
void Backtrak(int t)
{
if(t>n) sum++;
else
for(int i=1; i <= n; i++)
{
x[t] =i;
if(Place(t))Backtrak(t+1);
}
}
int main()
{
int nn;
while(scanf("%d",&nn)!=EOF)
{
n=nn;
sum=0;
for(int i=0;i<=n;i++)
x[i]=0;
Backtrak(1);
printf("%d\n",sum);
}
}
這段代碼有必要解釋一下,Place(int)即嘗試看是否可以,如果不可以則回退到t+1層,再嘗試其他的組合。
這里也道出了回溯算法的核心思想:但當(dāng)探索到某一步時(shí),發(fā)現(xiàn)原先選擇并不優(yōu)或達(dá)不到目標(biāo),就退回一步重新選擇
算法實(shí)踐:
問題描述:在一個(gè)n*n的網(wǎng)格里,每個(gè)網(wǎng)格可能為“墻壁”(用‘X'表示)和“街道”(用‘.'表示)?,F(xiàn)在在街道放置碉堡,每個(gè)碉堡可以向上下左右四個(gè)方向開火,子彈射程無限遠(yuǎn)。墻壁可以阻擋子彈。問最多能放置多少個(gè)碉堡,使它們彼此不會(huì)互相摧毀。
如下面四張圖,墻壁用黑正方形表示,街道用空白正方形表示,圓球就代表碉堡。1,2,3是正確的,4,5是錯(cuò)誤的。以為4,5里面在某一行或者某一列有兩個(gè)碉堡,這樣他們就會(huì)互相攻擊了。意思明白了嗎?可能我的表達(dá)很不清晰,呵呵….
輸入輸出示例
Sample input:
——————輸入的n值
.X..
....
XX..
....
XX
.X
.X.
X.X
.X.
....
....
....
....
Sample output:
初拿到這個(gè)問題,你會(huì)不會(huì)想到回溯算法呢?有人說遍歷墻的位置,然后再墻的上下左右四個(gè)格子放置碉堡會(huì)得到最優(yōu)解,這個(gè)我沒有驗(yàn)證過,細(xì)細(xì)的用筆畫了畫,好像是這么回事,但是很多時(shí)候要知道最優(yōu)解用什么方法是很難發(fā)現(xiàn)的,利用通用解題方法回溯法,我們可以在一片茫然的時(shí)候開始我們的編程
首先我們來分析一下這個(gè)問題:使用回溯法,我們嘗試每一種可能放置的情況,然后進(jìn)行判斷是否滿足要求,若不滿足,嘗試放到下一個(gè)單元格,如此反復(fù),最終,我們將所有可能放置的情況全部遍歷出來了,連所有情況都出來了,難不成還找不到最優(yōu)解嗎?哈哈。。說做就做…
#include <stdio.h>
char map[4][4];
int best,n;
int canput(int row, int col)
{
int i;
for (i = row - 1; i >= 0; i--)
{
if (map[i][col] == 'o') return 0;
if (map[i][col] == 'x') break;
}
for (i = col - 1; i >= 0; i--)
{
if (map[row][i] == 'o') return 0;
if (map[row][i] == 'x') break;
}
return 1;
}
void solve(int k,int tot)
{
int x,y;
if(k==n*n)
{
if(tot>best)
{
best=tot; return;
}
}
else
{
x=k/n;
y=k%n;
if((map[x][y]=='.') && (canput(x,y) ) )
{
map[x][y]='o';
solve(k+1,tot+1);
map[x][y]='.';
}
solve(k+1,tot);
}
}
int main()
{
int i,j;
scanf("%d",&n);
while(n>0)
{
for(i=0;i< n;i++)
for(j=0;j< n;j++)
scanf("%1s",&map[i][j]);
best=0;
solve(0,0);
printf("%d\n",best);
n=0;
scanf("%d",&n);
}
return 0;
}
對(duì)上面的代碼做一下點(diǎn)解釋,canput是做檢驗(yàn)的,檢驗(yàn)放在某個(gè)地點(diǎn)到底行不行得通,solve才是真正進(jìn)行遞歸回溯的函數(shù)。。
上一篇:使用代碼驗(yàn)證linux子進(jìn)程與父進(jìn)程的關(guān)系
欄 目:C語言
下一篇:c語言網(wǎng)絡(luò)編程-標(biāo)準(zhǔn)步驟(比較簡(jiǎn)單)
本文標(biāo)題:算法詳解之回溯法具體實(shí)現(xiàn)
本文地址:http://mengdiqiu.com.cn/a1/Cyuyan/3780.html
您可能感興趣的文章
- 01-10求子數(shù)組最大和的解決方法詳解
- 01-10深入二叉樹兩個(gè)結(jié)點(diǎn)的最低共同父結(jié)點(diǎn)的詳解
- 01-10數(shù)據(jù)結(jié)構(gòu)課程設(shè)計(jì)- 解析最少換車次數(shù)的問題詳解
- 01-10數(shù)據(jù)結(jié)構(gòu)課程設(shè)計(jì)-用棧實(shí)現(xiàn)表達(dá)式求值的方法詳解
- 01-10HDOJ 1443 約瑟夫環(huán)的最新應(yīng)用分析詳解
- 01-10使用C++實(shí)現(xiàn)全排列算法的方法詳解
- 01-10如何查看進(jìn)程實(shí)際的內(nèi)存占用情況詳解
- 01-10深入Main函數(shù)中的參數(shù)argc,argv的使用詳解
- 01-10APUE筆記之:進(jìn)程環(huán)境詳解
- 01-10深入第K大數(shù)問題以及算法概要的詳解


閱讀排行
- 1C語言 while語句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語言實(shí)現(xiàn)“百馬百擔(dān)”問題方法
- 4C語言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 04-02c語言函數(shù)調(diào)用后清空內(nèi)存 c語言調(diào)用
- 04-02func函數(shù)+在C語言 func函數(shù)在c語言中
- 04-02c語言的正則匹配函數(shù) c語言正則表達(dá)
- 04-02c語言用函數(shù)寫分段 用c語言表示分段
- 04-02c語言中對(duì)數(shù)函數(shù)的表達(dá)式 c語言中對(duì)
- 04-02c語言編寫函數(shù)冒泡排序 c語言冒泡排
- 04-02c語言沒有round函數(shù) round c語言
- 04-02c語言分段函數(shù)怎么求 用c語言求分段
- 04-02C語言中怎么打出三角函數(shù) c語言中怎
- 04-02c語言調(diào)用函數(shù)求fibo C語言調(diào)用函數(shù)求
隨機(jī)閱讀
- 04-02jquery與jsp,用jquery
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-11ajax實(shí)現(xiàn)頁面的局部加載
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-10C#中split用法實(shí)例總結(jié)
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 01-10delphi制作wav文件的方法
- 01-10使用C語言求解撲克牌的順子及n個(gè)骰子