常用演算法設計方法 回溯法

2021-04-03 00:47:59 字數 4443 閱讀 3714

常用演算法設計方法——回溯法

」);

} a[i]++;

continue;

} else

}   while (1)

} main()

【問題】   填字遊戲

問題描述:在3×3個方格的方陣中要填入數字1到n(n≥10)內的某9個數字,每個方格填乙個整數,似的所有相鄰兩個方格內的兩個整數之和為質數。試求出所有滿足這個要求的各種數字填法。

可用試探發找到問題的解,即從第乙個方格開始,為當前方格尋找乙個合理的整數填入,並在當前位置正確填入後,為下一方格尋找可填入的合理整數。如不能為當前方格找到乙個合理的可填證書,就要回退到前一方格,調整前一方格的填入數。當第九個方格也填入合理的整數後,就找到了乙個解,將該解輸出,並調整第九個的填入的整數,尋找下乙個解。

為找到乙個滿足要求的9個數的填法,從還未填乙個數開始,按某種順序(如從小到大的順序)每次在當前位置填入乙個整數,然後檢查當前填入的整數是否能滿足要求。在滿足要求的情況下,繼續用同樣的方法為下一方格填入整數。如果最近填入的整數不能滿足要求,就改變填入的整數。如對當前方格試盡所有可能的整數,都不能滿足要求,就得回退到前一方格,並調整前一方格填入的整數。如此重複執行擴充套件、檢查或調整、檢查,直到找到乙個滿足問題要求的解,將解輸出。

回溯法找乙個解的演算法:

while ((!ok||m!=n)&&(m!=0))

if (m!=0)   輸出解;

else      輸出無解報告;

} 如果程式要找全部解,則在將找到的解輸出後,應繼續調整最後位置上填放的整數,試圖去找下乙個解。相應的演算法如下:

回溯法找全部解的演算法:

else   擴充套件;

} else      調整;

ok=檢查前m個整數填放的合理性;

}   while (m!=0);

} 為了確保程式能夠終止,調整時必須保證曾被放棄過的填數序列不會再次實驗,即要求按某種有許模型生成填數序列。給解的候選者設定乙個被檢驗的順序,按這個順序逐一形成候選者並檢驗。從小到大或從大到小,都是可以採用的方法。如擴充套件時,先在新位置填入整數1,調整時,找當前候選解中下乙個還未被使用過的整數。將上述擴充套件、調整、檢驗都編寫成程式,細節見以下找全部解的程式。

【程式】

# include

# define    n   12

void write(int a[ ])

scanf(「%*c」);

} int b[n+1];

int a[10];

int isprime(int m)

; if (m==1||m%2=0)   return 0;

for (i=0;primes[i]>0;i++)

if (m==primes[i])   return 1;

for (i=3;i*i<=m;)

return 1;

} int checkmatrix[ ][3]=,,,,,

,,,};

int selectnum(int start)

int check(int pos)

int extend(int pos)

int change(int pos)

void find()

else   pos=extend(pos);

else   pos=change(pos);

ok=check(pos);

}   while (pos>=0)

} void main()

【問題】   n皇后問題

問題描述:求出在乙個n×n的棋盤上,放置n個不能互相捕捉的西洋棋「皇后」的所有布局。

這是**於西洋棋的乙個問題。皇后可以沿著縱橫和兩條斜線4個方向相互捕捉。如圖所示,乙個皇后放在棋盤的第4行第3列位置上,則棋盤上凡打「×」的位置上的皇后就能與這個皇后相互捕捉。

1   2   3   4   5   6   7   8

×         ×      

×      ×      ×         

×   ×   ×            

×   ×   q   ×   ×   ×   ×   ×

×   ×   ×            

×      ×      ×         

×         ×      

×            ×   

從圖中可以得到以下啟示:乙個合適的解應是在每列、每行上只有乙個皇后,且一條斜線上也只有乙個皇后。

求解過程從空配置開始。在第1列至第m列為合理配置的基礎上,再配置第m+1列,直至第n列配置也是合理時,就找到了乙個解。接著改變第n列配置,希望獲得下乙個解。另外,在任一列上,可能有n種配置。開始時配置在第1行,以後改變時,順次選擇第2行、第3行、…、直到第n行。當第n行配置也找不到乙個合理的配置時,就要回溯,去改變前一列的配置。得到求解皇后問題的演算法如下:

else   擴充套件當前候選接至下一列;

else   改變之,形成下乙個候選解;

good=檢查當前候選解的合理性;

} while (m!=0);

} 在編寫程式之前,先確定邊式棋盤的資料結構。比較直觀的方法是採用乙個二維陣列,但仔細觀察就會發現,這種表示方法給調整候選解及檢查其合理性帶來困難。更好的方法乃是盡可能直接表示那些常用的資訊。對於本題來說,「常用資訊」並不是皇后的具體位置,而是「乙個皇后是否已經在某行和某條斜線合理地安置好了」。因在某一列上恰好放乙個皇后,引入乙個一維陣列(col[ ]),值col[i]表示在棋盤第i列、col[i]行有乙個皇后。例如:col[3]=4,就表示在棋盤的第3列、第4行上有乙個皇后。另外,為了使程式在找完了全部解後回溯到最初位置,設定col[0]的初值為0當回溯到第0列時,說明程式已求得全部解,結束程式執行。

為使程式在檢查皇后配置的合理性方面簡易方便,引入以下三個工作陣列:

(1)   陣列a[ ],a[k]表示第k行上還沒有皇后;

(2)   陣列b[ ],b[k]表示第k列右高左低斜線上沒有皇后;

(3)   陣列 c[ ],c[k]表示第k列左高右低斜線上沒有皇后;

棋盤中同一右高左低斜線上的方格,他們的行號與列號之和相同;同一左高右低斜線上的方格,他們的行號與列號之差均相同。

初始時,所有行和斜線上均沒有皇后,從第1列的第1行配置第乙個皇后開始,在第m列col[m]行放置了乙個合理的皇后後,準備考察第m+1列時,在陣列a[ ]、b[ ]和c[ ]中為第m列,col[m]行的位置設定有皇后標誌;當從第m列回溯到第m-1列,並準備調整第m-1列的皇后配置時,清除在陣列a[ ]、b[ ]和c[ ]中設定的關於第m-1列,col[m-1]行有皇后的標誌。乙個皇后在m列,col[m]行方格內配置是合理的,由陣列a[ ]、b[ ]和c[ ]對應位置的值都為1來確定。細節見以下程式:

【程式】

# include   

# include   

# define   maxn   20

int n,m,good;

int col[maxn+1],a[maxn+1],b[2*maxn+1],c[2*maxn+1];

void main()

col[m]++;

} else

else

col[m]++;

} good=a[col[m]]&&b[m+col[m]]&&c[n+m-col[m]];

} while (m!=0);

} 試探法找解演算法也常常被編寫成遞迴函式,下面兩程式中的函式queen_all()和函式queen_one()能分別用來解皇后問題的全部解和乙個解。

【程式】

# include   

# include   

# define   maxn   20

int n;

int col[maxn+1],a[maxn+1],b[2*maxn+1],c[2*maxn+1];

void main()

void queen_all(int k,int n)

queen_all(k+1,n);

a[i]=b[k+i]=c[n+k-i];

} }

採用遞迴方法找乙個解與找全部解稍有不同,在找乙個解的演算法中,遞迴演算法要對當前候選解最終是否能成為解要有回答。當它成為最終解時,遞迴函式就不再遞迴試探,立即返回;若不能成為解,就得繼續試探。設函式queen_one()返回1表示找到解,返回0表示當前候選解不能成為解。細節見以下函式。

【程式】

# define   maxn   20

int n;

int col[maxn+1],a[maxn+1],b[2*maxn+1],c[2*maxn+1];

int queen_one(int k,int n)

{   int i,found;

i=found=0;

while (!found&&i

演算法設計 回溯法

t1 設某一機器由n個部件組成,部件編號為1n,每一種部件都可以從m個不同的 商處購得,商編號為1m。設wij是從 商j處購得的部件i的重量,cij是相應的 對於給定的機器部件重量和機器部件 計算總 不超過d的最小重量機器設計。注意 輸出結果中第一行最後沒有空格。比如下面的輸出樣例中1 3 1後面沒...

常用演算法之回溯法

回溯演算法實際上乙個類似列舉的搜尋嘗試過程,主要是在搜尋嘗試過程中尋找問題的解,當發現已不滿足求解條件時,就 回溯 返回,嘗試別的路徑。回溯法是一種選優搜尋法,按選優條件向前搜尋,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術為回溯法,...

常用演算法之 回溯法

回溯法,又稱試探法,是常用的,基本的優選搜尋方法。常用於解決這一類問題 給定一定約束條件f 該約束條件常用於後面的剪枝 下求問題的乙個解或者所有解。回溯法其實是暴力列舉的一種改進,因為其會聰明的filter掉不合適的分支,大大減少了無謂的列舉。若某問題的列舉都是可行解得話,也就是沒有剪枝發生,那麼回...