回溯法有“通用的解题法”之称,可以系统的搜索一个问题的所有解或任一解。它在问题的解空间中按深度优先策略,从根节点出发,搜索解空间树。算法搜索至解空间树的任一节点时,先判断该节点是否包含问题的解,如果肯定不包含则跳过对以该节点为根的子树的搜索,回到其父节点回溯。否则,进入该子树,继续按深度优先策略搜索。
与蛮力算法(穷举法)不同在于:回溯算法多了“观望”,可行则前进,不可行则回溯
利用回溯法解问题时一般按以下三步骤:
(1)定义搜索问题的解向量和每个分量的取值范围,解向量是<x1,x2...xn>,确定xi的取值集合,i=1,2....
(2)在<x1,x2...xk-1>确定如何计算xk的取值集合Sk
(3)确定节点儿子的排列规则
(4)判断是否满足多米诺性质
(5)确定每个节点的约束条件
(6)确定搜索策略(深度优先?宽度优先?或者其他)
(7)确定存储搜索路径的数据结构
最优装载问题的回溯算法实现:
二、回溯法应用——装载问题
一批集装箱共n个要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量为Wi且W1+W2+……+Wn<=c1+c2;试确定一个合理的装载方案使这n个集装箱装上这两艘轮船。
#include<stdlib.h> #include<stdio.h> #include<iostream> using namespace std; typedef int Status; typedef int Type; int n = 0; //集装箱数 Type *x = (Type*)malloc((50)*sizeof(Type));//当前解 Type *bestx = (Type*)malloc((50)*sizeof(Type));//当前最优解 Type c = 0, //第一艘轮船的载重量 cw = 0, //当前载重量 bestw = 0, //当前最优载重量 r = 0, *w = (Type*)malloc((50)*sizeof(Type)); //集装箱重量数组 int Backtrack(int i)//搜索第i层节点 { int j_index; /*如果到达叶结点,则判断当前的cw,如果比前面得到的最优解bestw 好,则替换原最优 解。*/ if (i>n) { if (cw>bestw) { for (j_index = 1; j_index <= n; j_index++) bestx[j_index] = x[j_index]; bestw = cw; } return 1; } //搜索子树 r -= w[i]; if (cw + w[i] <= c)//搜索左子树,如果当前剩余空间可以放下当前物品也就是,cw + w[ i ] <= c { x[i] = 1; cw += w[i];//把当前载重 cw += w[ i ] Backtrack(i + 1);//递归访问其左子树,Backtrack( i + 1 ) cw -= w[i];//访问结束,回到调用点, cw - = w[ i ] } if (cw + r>bestw)//搜索右子树 { x[i] = 0; Backtrack(i + 1); } r += w[i]; } Type* Initiate() { int index = 1; printf("输入集装箱个数:"); scanf("%d", &n); printf("输入轮船载重量:"); scanf("%d", &c); while (index <= n)//数组从1号单元开始存储 { printf("输入集装箱%d 的重量:", index); scanf("%d", &w[index]); index++; } bestw = 0; cw = 0; r = 0; for (index = 1; index <= n; index++) r += w[index]; //初始时r 为全体物品的重量和 printf("n=%d c=%d cw=%d bestw=%d r=%d\n", n, c, cw, bestw, r); for (index = 1; index <= n; index++) { printf("w[%d]=%d ", index, w[index]); } printf("\n"); return w; } int main() { int i; Initiate(); //计算最优载重量 Backtrack(1); for (i = 1; i <= n; i++) { printf("%d ", w[i]); printf("%d\n", bestx[i]); } return bestw; }
转载必须注明出处