数据结构与算法之美(笔记22)回溯算法
如何理解回溯算法?
回溯的处理思想,有点类似枚举搜索。我们枚举所有的解,找到满足期望的解。为了有规律地枚举所有的解,避免遗漏或者重复,我们把问题求解的过程分为好几个阶段。每个阶段,我们都会面对一个分岔路,我们随意选择一条路走,当发现这条路不通的时候,就回退到上一个岔路口,另选一种走法继续走。
八皇后问题
我们有一个8x8的棋盘,希望往里放8个棋子(皇后),每个棋子所在的行、列、对角线都不能有另一个棋子。
第一个图是符合要求的一种摆法,右边是不符合的一种摆法。
我们把这个问题分成8个阶段,依次将棋子放在第一行、第二行......第八行。在放置过程中,我们不停检查当前的方法,是否满足要求。如果满足,则跳到下一行继续放置棋子;如果不满足,就换另一种方法。
回溯算法非常适合递归代码实现,这里给出代码的实现:
int chess[8];//用来保存结果
void printqueue(int* result){
for(int i=0;i<8;++i){
for(int j=0;j<8;++j){
if(result[i] == j) cout << "Q ";
else cout << "* ";
}
cout << endl;
}
cout << endl;
}
bool isOk(int row,int col){
int leftup = col-1;
int rightup = col+1;
for(int i=row-1;i>=0;--i){// 逐渐向上考察每一行
if(chess[i] == col) return false;
if(leftup >=0){
if(chess[i] == leftup) return false;
}
if(rightup < 8){
if(chess[i] == rightup) return false;
}
leftup--;
rightup++;
}
return true;
}
void cal8queue(int row){
if(row == 8){// 递归终止条件
printqueue(chess);
return;
}
for(int col=0;col<8;++col){// 每行(每个阶段)有8个方法
if(isOk(row,col)){// 检查这个摆法是否正确
chess[row] = col;
cal8queue(row+1);
}
}
}
int main(){
for(int i=0;i<8;++i){
chess[i] = -1;
}
cal8queue(0);
}
0-1背包问题
问题是这样的:我们有一个背包,背包总的承载重量是Wkg。现在我们有n个物体,每个物体的重量不等,并且不可分割。我们现在在期望选择几件物品,装载在背包中。在不超过背包所能够装载的前提下,如何让背包中额物品的总重量最大?
刚看到这个问题,是不是立马想到贪心算法?实际上,在贪心算法的那个背包问题,物体是可以分割的,我们可以选择一部分放入背包,但这里是不可分割的,要么装,要么不装,所以叫作0-1背包问题,显然不能够使用贪心算法来解决了。
我们看如何利用回溯算法来解决。我们有n个物品,我们把它看成n个阶段,每个阶段有两种选择,我们通过枚举所有的装法,找到其中最大的一种,就是正确答案了。
这里给出代码的实现:
/* cur_n表示现在考察到哪个物品,bag_weight表示当前背包中的重量,n是物体的个数,items是每个物体的重量,weight是限制的重量数*/
int max_w = 0;
void bag_01(int cur_n,int bag_weight,int n,int* items,int weight){
if(bag_weight == weight||cur_n == n){// 递归终止条件,这里的bag_weight == weight是一个搜索剪枝的技巧,减少递归次数
if(bag_weight > max_w) max_w = bag_weight;// 保存最大的bag_weight
return;
}
bag_01(cur_n+1,bag_weight,n,items,weight);// 不装的情况
if(bag_weight+items[cur_n] <= weight){// 满足期望解的条件
bag_01(cur_n+1,bag_weight+items[cur_n],n,items,weight);// 装的情况
}
bag_01(cur_n+1,bag_weight+items[cur_n],n,items,weight);// 装的情况
}
int main(){
int weight = 10;
int n = 5;
int *items = new int[5];
for(int i=0;i<5;++i){
items[i] = i*2+1;
}
bag_01(0,0,n,items,weight);
cout << max_w << endl;
}