回溯算法总结

来源:互联网 发布:网上谈恋爱软件 编辑:程序博客网 时间:2024/06/16 07:24

回溯法又称为试探法,可以系统的搜索一个问题的所有解或任一解。 

适用情况:当需要找出问题的解集或者要求回答什么解是满足某些约束条件的最佳解。

一般步骤:(1) 针对给定的问题,明确问题的解空间;

                  (2) 确定易于搜索的解空间结构;

                  (3) 以深度优先方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索。

经典例子(装载问题)

1.问题描述:给定n个集装箱要装上一艘载重量为c的轮船,其中集装箱i的重量为wi。

                     要求确定在不超过轮船载重量的前提下,将尽可能多的集装箱装上轮船。

2.输入:每组测试数据,第1行有2个整数c和n。c是轮船的载重量(0<c<30000),n是集装箱的个数(n≤20)。

                                       第2行有n个整数w1, w2, …, wn,分别表示n个集装箱的重量。

  输出:对每个测试例,输出两行,第1行是装载到轮船的最大载重量,第2行是集装箱的编号。

3.思路:集装箱问题是从n个集装箱里选择一部分集装箱,假设解向量为X=(x1, x2, …, xn),其中xi∈{0, 1}, xi =1表示集装箱i装上轮船, xi =0表示集装箱i不装上轮船。

利用回溯法解装载问题,其解空间是一棵子集树。

可行性约束函数为:cw(t)=w1*x1+w2*x2+…+wt*xt<=c,其中cw(t)表示从根结点到第t层结点为止装入轮船的重量,即部分解(x1,x2,…,xt)的重量。当cw(t)>c时,表示该子树中所有结点都不满足约束条件,可将该子树剪去。                  

4.代码:

#include<iostream>
using namespace std;
#define NUM 100
int n;                    //集装箱的数量
int c;                    //轮船的载重量
int w[NUM];               //集装箱的重量数组
int x[NUM];               //当前搜索的解向量
int cw;                   //当前轮船的载重量
int bestw;                //当前最优载重量
int bestx[NUM];           //当前最优解
int r;                    //剩余集装箱的重量
void Backtrack(int t){
    //到达叶子结点
    if(t>n){
        //更新最优解
        if(cw>bestw){
            for(int i=1;i<=n;i++){
                bestx[i]=x[i];
                bestw=cw;
            }
            return;
        }
    }
    //更新剩余集装箱的重量
    r-=w[t];
    //搜索左子树
    if(cw+w[t]<=c){
        x[t]=1;
        cw+=w[t];
        Backtrack(t+1);
        cw-=w[t];
    }
    //搜索右子树
    if(cw+r>bestw){
        x[t]=0;
        Backtrack(t+1);
    }
    r+=w[t];//恢复状态
}
int main(){
    cin>>c>>n;
    for(int i=1;i<=n;i++){
        cin>>w[i];
    }
    //剩余集装箱的重量r初始化
    r=0;
    for(int i=1;i<=n;i++){
        r+=w[i];
    }
    int t=1;
    Backtrack(t);
    cout<<bestw<<endl;
    for(int i=1;i<=n;i++){
        if(bestx[i]==1){
            cout<<i<<" ";
        }
    }
    cout<<endl;
}