回溯法(二)
- 回溯法回顧
- 揹包問題
- 八皇后問題
回溯法總結
回溯法思路的簡單描述是:把問題的解空間轉化成了圖或者樹的結構表示,然後使用深度優先搜索策略進行遍歷,遍歷的過程中記錄和尋找所有可行解或者最優解,同時爲了加快搜索速度,可使用分支定界方法或約束方法進行剪枝(在當前節點(擴展節點)處,先生成其所有的兒子節點(分支),然後再從當前的活節點(當前節點的子節點)表中選擇下一個擴展節點。爲了有效地選擇下一個擴展節點,加速搜索的進程,在每一個活節點處,計算一個函數值(限界),並根據函數值,從當前活節點表中選擇一個最有利的節點作爲擴展節點,使搜索朝着解空間上有最優解的分支推進,以便儘快地找出一個最優解。分支限界法解決了大量離散最優化的問題。)
揹包問題
問題描述: 給定n種物品和一揹包。物品i的重量是wi,其價值爲pi,揹包的容量爲C。問應如何選擇裝入揹包的物品,使得裝入揹包中物品的總價值最大?
分析: 問題是n個物品中選擇部分物品,可知,如n = 3時,問題的解空間是子集樹,可將其轉化爲二叉樹。搜索二叉樹即可。其約束函數爲每次選擇一個物品時其揹包的容量是否超過C。
代碼如下:
public class Main {
private static int N = 3;
private static int WW = 16;
private int curWeight = 0;
private int curValue = 0;
private int bestValue = 0;
private int[] x = {0,0,0}; //搜索標記
private int[] best = {0,0,0};
int[] w = {10,8,5};
int[] v = {5,4,1};
//遍歷搜索樹路徑
public void traverse(int t){
if(t > N-1){ //搜索到一個完整路徑
if(curValue > bestValue){ //找到一個更優解
bestValue = curValue;
for(int i = 0; i < N; i++)best[i] = x[i];
}
}else{ //未找到一個完整的路徑
for(int i =0; i <= 1; i++){
x[t] = i;
if(i ==0)traverse(t+1); //不放入揹包
else{ //放入揹包
if(curWeight+w[t] <= WW){
curWeight += w[t];
curValue += v[t];
traverse(t+1);
curWeight -= w[t];
curValue -= v[t];
}
}
}
}
}
public static void main(String[] args) {
Main main1 = new Main();
main1.traverse(0);
System.out.println("best:"+main1.bestValue);
System.out.print("best seq:");
for(int i = 0; i<N;i++){
System.out.print(main1.best[i]);
}
}
}
八皇后問題
**問題描述:**n×n格的棋盤上放置彼此不受攻擊的n個皇后。按照國際象棋的規則,皇后可以攻擊與之處在同一行或同一列或同一斜線上的棋子。則一共有多少种放法。
**分析:**n*n棋盤的每一層可以看作是一個選擇點,總共有n個選擇,即n叉樹。
代碼如下
public class Main {
private static int N = 8; //棋盤大小
private int[][] chess = new int[N][N]; //棋盤
private int way = 0; //初始化
/**
* 判斷在棋盤
* @param x
* @param y
* @return
*/
public boolean isFeasible(int row,int col){
if(row >=N || row <0 || col >=N || col<0)return false;
if(chess[row][col] != 0)return false;
for(int i = 0; i < N; i++){ //行列不衝突
if(chess[row][i] !=0 || chess[i][col] != 0)return false;
}
for(int i = 0; i<N;i++){ //斜線不衝突
if((row-i)>=0 && (col-i)>=0) //位置合法
{
if(chess[row-i][col-i] != 0)//此處已有皇后,衝突
return false;
}
//左下角
if((row+i)<N && (col-i)>=0)
{
if(chess[row+i][col-i] != 0)
return false;
}
//右上角
if((row-i)>=0 && (col+i)<N)
{
if(chess[row-i][col+i] != 0)
return false;
}
//右下角
if((row+i)<N && (col+i)<N)
{
if(chess[row+i][col+i] != 0)
return false;
}
}
return true;
}
public void queen(int n){ //構成搜索樹
if(n >=N){
way++;
}
else{
for(int i = 0; i<N;i++){ //搜索第n層樹的路徑
if(isFeasible(n,i)){ //判斷第index行,第n列
chess[n][i] = 1;
queen(n+1);
chess[n][i] = 0;
}
}
}
}
public static void main(String[] args) {
Main main1 = new Main();
main1.queen(0);
System.out.println("總共有"+main1.way+"中放法");
}
}