数据结构与算法之美(笔记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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值