当前位置:文档之家› 算法之回溯法实现

算法之回溯法实现

实验4回溯法实现一、实验目标:1.熟悉回溯法应用场景及实现的基本方法步骤;2.学会回溯法的实现方法和分析方法:二、实验内容1.旅行售货员问题:当结点数为4,权重矩阵为0110239429340660,求最优路径及开销。

2.0-1背包问题:对于n=5,C=10,vi={6,3,5,4,6},wi={2,2,6,5,4},计算xi及最优价值V。

分别利用动态规划、回溯法和分支限界法解决此问题,比较并分析这三种算法实现!三、实验过程1.源代码旅行售货员问题(回溯法):#include<iostream>using namespace std;class travel //回溯{friend int TSP(int **, int[], int, int);private:void Backtrack(int i);int n, //顶点数*x,*bestx;int **a,cc,bestc,NoEdge;};void S a, int b){int temp;temp = a;a = b;b = temp;return;}void travel::Backtrack(int i){if (i == n){if (a[x[n - 1]][x[n]] != NoEdge && a[x[n]][1] != NoEdge &&(cc + a[x[n - 1]][x[n]] + a[x[n]][1]) < bestc || bestc == NoEdge){for (int j = 1; j <= n; j++) bestx[j] = x[j];bestc = cc + a[x[n - 1]][x[n]] + a[x[n]][1];}}else{for (int j = i; j <= n; j++){if (a[x[i - 1]][j] != NoEdge && a[x[n]][1] != NoEdge&& (cc + a[x[i - 1]][x[j]] < bestc || bestc == NoEdge)) {swap(x[i], x[j]);cc += a[x[i - 1]][x[i]];Backtrack(i + 1);cc -= a[x[i - 1]][x[i]];swap(x[i], x[j]);}}}}int TSP(int** a,int v[], int n, int NoEdge){travel Y;Y.x = new int[n + 1];for (int i = 1; i <= n; i++)Y.x[i] = i;Y.a = a;Y.n = n;Y.bestc = NoEdge;Y.bestx = v; = 0;Y.NoEdge = NoEdge;Y.Backtrack(2);delete[] Y.x;return Y.bestc;}int main(){int const max = 10000;cout << "请输入节点数:" << endl;int n;cin >> n;int *v = new int[n];//保存路径int NoEdge = 0;int **p = new int*[max];for (int i = 0; i < n+1; i++)//生成二维数组p[i] = new int[n+1];cout << "请依次输入各城市之间的路程:" << endl;for (int i = 0; i < n; i++)for (int j = 0; j < n; j++)cin >> p[i+1][j+1];cout << "最短路径长度:" << TSP(p, v, 4, 1000) << endl;cout << "路径为:";for (int i = 1; i < 5; i++)cout << v[i] <<' ';cout << endl;return 0;}运行截图:旅行售货员问题(分支限界法):#include<iostream>using namespace std;#define MAX_CITY_NUMBER 10 //城市最大数目#define MAX_COST 1000 //两个城市之间费用的最大值int City_Graph[MAX_CITY_NUMBER][MAX_CITY_NUMBER];//表示城市间边权重的数组int City_Size; //表示实际输入的城市数目int Best_Cost; //最小费用int Best_Cost_Path[MAX_CITY_NUMBER];//结点typedef struct Node {int lcost; //优先级int cc; //当前费用int rcost; //剩余所有结点的最小出边费用的和int s; //当前结点的深度,也就是它在解数组中的索引位置int x[MAX_CITY_NUMBER]; //当前结点对应的路径struct Node* pNext; //指向下一个结点}Node;//堆typedef struct MiniHeap {Node* pHead; //堆的头}MiniHeap;//初始化void InitMiniHeap(MiniHeap* pMiniHeap) {pMiniHeap->pHead = new Node;pMiniHeap->pHead->pNext = NULL;}//入堆void put(MiniHeap* pMiniHeap, Node node) {Node* next;Node* pre;Node* pinnode = new Node; //将传进来的结点信息copy一份保存//这样在函数外部对node的修改就不会影响到堆了pinnode->cc = ;pinnode->lcost = node.lcost;pinnode->pNext = node.pNext;pinnode->rcost = node.rcost;pinnode->s = node.s;pinnode->pNext = NULL;for (int k = 0; k<City_Size; k++) {pinnode->x[k] = node.x[k];}pre = pMiniHeap->pHead;next = pMiniHeap->pHead->pNext;if (next == NULL) {pMiniHeap->pHead->pNext = pinnode;}else {while (next != NULL) {if ((next->lcost) >(pinnode->lcost)) { //发现一个优先级大的,则置于其前面pinnode->pNext = pre->pNext;pre->pNext = pinnode;break; //跳出}pre = next;next = next->pNext;}pre->pNext = pinnode; //放在末尾}}//出堆Node* RemoveMiniHeap(MiniHeap* pMiniHeap) {Node* pnode = NULL;if (pMiniHeap->pHead->pNext != NULL) {pnode = pMiniHeap->pHead->pNext;pMiniHeap->pHead->pNext = pMiniHeap->pHead->pNext->pNext;}return pnode;}//分支限界法找最优解void Traveler() {int i, j;int temp_x[MAX_CITY_NUMBER];Node* pNode = NULL;int miniSum; //所有结点最小出边的费用和int miniOut[MAX_CITY_NUMBER];//保存每个结点的最小出边的索引MiniHeap* heap = new MiniHeap; //分配堆InitMiniHeap(heap); //初始化堆miniSum = 0;for (i = 0; i<City_Size; i++) {miniOut[i] = MAX_COST; //初始化时每一个结点都不可达for (j = 0; j<City_Size; j++) {if (City_Graph[i][j]>0 && City_Graph[i][j]<miniOut[i]) {//从i到j可达,且更小miniOut[i] = City_Graph[i][j];}}if (miniOut[i] == MAX_COST) {// i 城市没有出边Best_Cost = -1;return;}miniSum += miniOut[i];}for (i = 0; i<City_Size; i++) { //初始化的最优路径就是把所有结点依次走一遍Best_Cost_Path[i] = i;}Best_Cost = MAX_COST; //初始化的最优费用是一个很大的数pNode = new Node; //初始化第一个结点并入堆pNode->lcost = 0; //当前结点的优先权为0 也就是最优pNode->cc = 0; //当前费用为0(还没有开始旅行)pNode->rcost = miniSum; //剩余所有结点的最小出边费用和就是初始化的miniSumpNode->s = 0; //层次为0pNode->pNext = NULL;for (int k = 0; k<City_Size; k++) {pNode->x[k] = Best_Cost_Path[k]; //第一个结点所保存的路径也就是初始化的路径}put(heap, *pNode); //入堆while (pNode != NULL && (pNode->s) < City_Size - 1) {//堆不空不是叶子for (int k = 0; k<City_Size; k++) {Best_Cost_Path[k] = pNode->x[k]; //将最优路径置换为当前结点本身所保存的}/** * pNode 结点保存的路径中的含有这条路径上所有结点的索引* * x路径中保存的这一层结点的编号就是x[City_Size-2]* * 下一层结点的编号就是x[City_Size-1]*/if ((pNode->s) == City_Size - 2) { //是叶子的父亲int edge1 = City_Graph[(pNode->x)[City_Size - 2]][(pNode->x)[City_Size - 1]];int edge2 = City_Graph[(pNode->x)[City_Size - 1]][(pNode->x)[0]];if (edge1 >= 0 && edge2 >= 0 && (pNode->cc + edge1 + edge2) < Best_Cost) {//edge1 -1 表示不可达//叶子可达起点费用更低Best_Cost = pNode->cc + edge1 + edge2;pNode->cc = Best_Cost;pNode->lcost = Best_Cost; //优先权为Best_CostpNode->s++; //到达叶子层}}else { //内部结点for (i = pNode->s; i<City_Size; i++) { //从当前层到叶子层if (City_Graph[pNode->x[pNode->s]][pNode->x[i]] >= 0) { //可达//pNode的层数就是它在最优路径中的位置int temp_cc = pNode->cc + City_Graph[pNode->x[pNode->s]][pNode->x[i]];int temp_rcost = pNode->rcost - miniOut[pNode->x[pNode->s]];//下一个结点的剩余最小出边费用和//等于当前结点的rcost减去当前这个结点的最小出边费用if (temp_cc + temp_rcost<Best_Cost) { //下一个结点的最小出边费用和小于当前的最优解,说明可能存在更优解for (j = 0; j<City_Size; j++) { //完全copy路径,以便下面修改temp_x[j] = Best_Cost_Path[j];}temp_x[pNode->x[pNode->s + 1]] = Best_Cost_Path[i];//将当前结点的编号放入路径的深度为s+1的地方temp_x[i] = Best_Cost_Path[pNode->s + 1];//将原路//径中的深度为s+1的结点编号放入当前路径的//相当于将原路径中的的深度为i的结点与深度W为s+1的结点交换Node* pNextNode = new Node;pNextNode->cc = temp_cc;pNextNode->lcost = temp_cc + temp_rcost;pNextNode->rcost = temp_rcost;pNextNode->s = pNode->s + 1;pNextNode->pNext = NULL;for (int k = 0; k<City_Size; k++) {pNextNode->x[k] = temp_x[k];}put(heap, *pNextNode);delete pNextNode;}}}}pNode = RemoveMiniHeap(heap);}for (int k = 0; k<City_Size; k++) {//复制路径Best_Cost_Path[k] = temp_x[k];}}int main() {cout << "请输入节点数:" << endl;cin >> City_Size;cout << "请依次输入各城市之间的距离" << endl;for (int i = 0; i < City_Size; i++)for (int j = 0; j < City_Size; j++){cin >> City_Graph[i][j];if (City_Graph[i][j] == 0)City_Graph[i][j] = 1000;}Traveler();cout <<"最短路径长度:" <<Best_Cost << endl;cout << "路径为:";for (int i = 0; i < 4; i++)cout << Best_Cost_Path[i]+1 << ' ';return 0;}运行截图:0-1背包问题(动态规划):#include<iomanip>#include<iostream>using namespace std;void knapsack(int v[], int *w, int c, int n, int**m) {int jmax = min(w[n] - 1, c); //1) 仅可选物品n时,容量为j的子问题的最优值for (int j = 0; j <= jmax; j++) m[n][j] = 0; //注意j为整数for (int j = w[n]; j <= c; j++) m[n][j] = v[n];for (int i = n - 1; i>0; i--) { //2) 逐步增加物品数至n及容量至cjmax = min(w[i] - 1, c); //仅可选物品i时,容量为j的子问题的最优值for (int j = 0; j <= jmax; j++) m[i][j] = m[i + 1][j];for (int j = w[i]; j <= c; j++) m[i][j] = max(m[i + 1][j], m[i + 1][j - w[i]] + v[i]);}m[1][c] = m[2][c]; //处理物品1,最后一件的边界情况if (c >= w[1]) m[1][c] = max(m[1][c], m[2][c - w[1]] + v[1]); }void traceback(int **m, int *w, int c, int n, int *x){for (int i = 1; i<n; i++) {if (m[i][c] == m[i + 1][c])x[i] = 0; //二者相等说明物品i不装入else {x[i] = 1;c = c - w[i];}x[n] = (m[n][c]) ? 1 : 0;}}int max(int a, int b){return a > b ? a : b;}int min(int a, int b){return a > b ? b : a;}int main(){int n, c;cout << "请输入物品数:" << endl;cin >> n;cout << "请输入背包容量:" << endl;cin >> c;int *v = new int[n + 1];int *w = new int[n + 1];cout << "请输入物品重量数组:" << endl;for (int i = 1; i < n + 1; i++)cin >> w[i];cout << "请输入物品价值数组:" << endl;for (int i = 1; i < n + 1; i++)cin >> v[i];int **p = new int*[n + 1];//子问题最优解for (int i = 1; i < n + 1; i++)p[i] = new int[c+1];int *x = new int[n + 1];knapsack(v, w, c, n, p);traceback(p, w, c, n, x);cout << "m(i,j):" << endl;for (int i = 5; i > 0; i--){for (int j = 0; j < 11; j++)cout <<setw(2)<< p[i][j] << ' ';cout << endl;}cout << "xi = ";for (int i = 1; i < 6; i++)cout << x[i] << ' ';cout << endl;cout << "V=" << p[1][c] << endl;for (int i = 1; i < n + 1; i++)//delete delete p[i];delete p;delete v, w;return 0;}运行截图:0-1背包问题(回溯法)#include<iostream>using namespace std;int n, c, bestp; //物品的个数,背包的容量,最大价值int p[10000], w[10000], x[10000], bestx[10000]; //物品的价值,物品的重量,x[i]暂存物品的选中情况,物品的选中情况void Backtrack(int i, int cp, int cw){ //cw当前包内物品重量,cp当前包内物品价值int j;if (i>n) //回溯结束{if (cp>bestp){bestp = cp;for (i = 0; i <= n; i++) bestx[i] = x[i];}}elsefor (j = 0; j <= 1; j++){x[i] = j;if (cw + x[i] * w[i] <= c){cw += w[i] * x[i];cp += p[i] * x[i];Backtrack(i + 1, cp, cw);cw -= w[i] * x[i];cp -= p[i] * x[i];}}}int main(){int i;bestp = 0;cout << "请输入物品数:" << endl;;cin >> n;cout << "请输入背包容量:" << endl;cin >> c;cout << "请输入物品重量数组:" << endl;;for (i = 1; i <= n; i++)cin >> w[i];cout << "请输入物品价值数组:" << endl;for (i = 1; i <= n; i++)cin >> p[i];Backtrack(1, 0, 0);cout << "V = "<< bestp << endl;cout << "xi = ";for (i = 1; i <= n; i++)cout << bestx[i] << ' ';cout << endl;system("pause");return 0;}运行截图:0-1背包问题(分支限界法):#include <iostream>using namespace std;class Object {friend int Knapsack(int *, int *, int, int, int *); public:int operator <= (Object a) const {return (d >= a.d);}private:int ID; //物品编号float d; //单位重量价值};class bbnode {friend class Knap;friend int Knapsack(int *, int *, int, int, int *);private:bbnode * parent; //指向父节点的指针int LChild; //如果是左儿子结点取1,也即说明该物品已装进背包};class HeapNode {friend class Knap;friend class MaxHeap;public:operator int()const { return uprofit; };private:int uprofit, //结点的价值上界profit; //结点所相应的价值int weight; //结点所相应的重量int level; //活结点在子集树中所处的层序号bbnode *ptr; //指向该活结点在子集树中相应结点的指针};class MaxHeap {public:MaxHeap(int maxElem){HeapElem = new HeapNode*[maxElem + 1]; //下标为0的保留capacity = maxElem;size = 0;}void InsertMax(HeapNode *newNode);HeapNode DeleteMax(HeapNode* &N);private:int capacity;int size;HeapNode **HeapElem;};//0-1背包问题的主类class Knap {//Knapsack主函数功能:解决初始化、求解最优值和最优解、回收内存friend int Knapsack(int *, int *, int, int, int *);public:int MaxKnapsack();private:MaxHeap * H;//Bound辅助Maxknapsack函数:计算结点价值上界int Bound(int i);//AddLiveNode辅助Maxknapsack函数:将活结点插入子集树和优先队列中void AddLiveNode(int up, int cp, int cw, int ch, int level);bbnode *E; //指向扩展结点的指针int c; //背包容量int n; //物品总数int *w; //物品重量数组(以单位重量价值降序)int *p; //物品价值数组(以单位重量价值降序)int cw; //当前装包重量int cp; //当前装包价值int *bestx; //最优解};void MaxHeap::InsertMax(HeapNode *newNode){//极端情况下暂未考虑,比如堆容量已满等等int i = 1;for (i = ++size; i / 2 > 0 && HeapElem[i / 2]->uprofit < newNode->uprofit;i /= 2){HeapElem[i] = HeapElem[i / 2];}HeapElem[i] = newNode;}HeapNode MaxHeap::DeleteMax(HeapNode *&N){//极端情况下暂未考虑if (size >0){N = HeapElem[1];//从堆顶开始调整int i = 1;while (i < size){if (((i * 2 + 1) <= size) && HeapElem[i * 2]->uprofit > HeapElem[i * 2 + 1]->uprofit){HeapElem[i] = HeapElem[i * 2];i = i * 2;}else{if (i * 2 <= size){HeapElem[i] = HeapElem[i * 2];i = i * 2;}elsebreak;}}if (i < size)HeapElem[i] = HeapElem[size];}size--;return *N;}int Knap::MaxKnapsack(){H = new MaxHeap(1000);bestx = new int[n + 1];//初始化,为处理子集树中的第一层做准备,物品i处于子集树中的第i层int i = 1; //生成子集树中的第一层的结点E = 0; //将首个扩展点设置为null,也就是物品1的父节点cw = 0;cp = 0;int bestp = 0; //当前最优值int up = Bound(1); // 选取物品1之后的价值上界//当选择左儿子结点时,上界约束up不用关心,重量约束wt需要考虑。

相关主题