0.回溯法的算法框架

A.简介

回溯法,又称试探法。一般需要遍历解空间,时间复杂度概况:子集树Ω(2^n),排序树Ω(n!),暴力法

B.回溯法解题三步骤

1)定义问题的解空间

如0-1背包问题,当n=3时,解空间是(0,0,0)、(0,0,1)、(0,1,0)、(0,1,1)、(1,0,0)、(1,0,1)、(1,1,0)、(1,1,1)。1代表选择该物品,0代表不选择该物品

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

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

C.子集树、排列树及其他

1)子集树

a.概念

当所给问题是从n个元素的集合S中找出S满足的某种性质的子集时,相应的解空间树称为子集树。例如,0-1背包问题,要求在n个物品的集合S中,选出几个物品,使物品在背包容积C的限制下,总价值最大(即集合S的满足条件<容积C下价值最大>的某个子集)。

另:子集树是从集合S中选出符合限定条件的子集,故每个集合元素只需判断是否(0,1)入选,因此解空间应是一颗满二叉树

b.回溯法搜索子集树的一般算法

void backtrack(int t)//t是当前层数
{if(t>n)//需要判断每一个元素是否加入子集,所以必须达到叶节点,才可以输出{output(x);}else{for(int i=0;i<=1;i++)//子集树是从集合S中,选出符合限定条件的子集,故每个元素判断是(1)否(0)选入即可(二叉树),因此i定义域为{0,1} {x[t]=i;//x[]表示是否加入点集,1表示是,0表示否if(constraint(t)&&bound(t))//constraint(t)和bound(t)分别是约束条件和限定函数 {backtrack(t+1);}}}
} 

2)排列树

a.概念

当问题是确定n个元素满足某种性质的排列时,相应的解空间称为排列树。排列树与子集树最大的区别在于,排列树的解包括整个集合S的元素,而子集树的解则只包括符合条件的集合S的子集。

b.回溯法搜素排列树的一般算法

void backtrack(int t)//t是当前层数
{if(t>n)//n是限定最大层数 {output(x);}else{for(int i=t;i<=n;i++)//排列树的节点所含的孩子个数是递减的,第0层节点含num-0个孩子,第1层节点含num-1个孩子,第二层节点含num-2个孩子···第num层节点为叶节点,不含孩子。即第x层的节点含num-x个孩子,因此第t层的i,它的起点为t层数,终点为num,第t层(根节点为空节点,除外),有num-t+1个亲兄弟,需要轮num-t+1回 {swap(x[t],x[i]);//与第i个兄弟交换位置,排列树一条路径上是没有重复节点的,是集合S全员元素的一个排列,故与兄弟交换位置后就是一个新的排列if(constraint(t)&&bound(t))//constraint(t)和bound(t)分别是约束条件和限定函数 {backtrack(t+1);}swap(x[i],x[t]);}}
} 

3)解空间非子集树,非排列树

递归:

void backtrack(int t)
{if(t>n){output(x);}else{for(int i=f(n,t);i<=g(n,t);i++){x[t]=h(i);if(constraint(t)&&bound(t)){backtrack(t+1);} }}
}

迭代:

void backtrack(int t)
{int t=1;while(t>0){if(f(n,t)<=g(n,t)){for(int i=f(n,t);i<=g(n,t);i++)//f和g是当前扩展节点的起止点 {x[t]=h(i);if(constraint(t)&&bound(t)){if(solution(t))//原先的递归出口 {output(x);}else{t++;//未解决问题,但在限定条件之内,走下一层 }}}}else{t--;//回到上一层 }}
} 

D.方法

先判断问题是子集树、排列树、还是非子集树非排列树,然后在做

1.装载问题

A.递归

根据节1给出的回溯模板,模仿所得,子集树

在编写时未考虑最优解问题(C1上装载越多,解越优),故本程序无法剪枝。若要考虑最优解,当总载重量小于最优载重量时,可以把右子树(0,不选择)全部剪去

#include<iostream>
#include<vector>
#include<cstdlib>
using namespace std;
int c1,c2,wsum;
vector<int> w;
vector<int> s;//是否加入点集C1
int random(int start,int end)
{return start+rand()%(end-start);
}
int s1=0;
void maxLoading(int c1w,int t)
{if(t>=w.size()){if((wsum-c1w)<=c2){cout<<"choice "<<++s1<<":\n\tc1 :";for(int i=0;i<s.size();i++){if(s[i]==1){cout<<i<<"\t";}}cout<<endl;}}else{for(int i=0;i<=1;i++){s[t]=i;if(i==1){c1w+=w[t];}if(c1w<=c1){maxLoading(c1w,t+1);}}}
}
int main()
{int num;cin>>num>>c1>>c2;wsum=0;for(int i=0;i<num;i++){int temp=random(1,100);w.push_back(temp);s.push_back(-1);wsum+=temp;cout<<temp<<"\t";}cout<<endl;maxLoading(0,0);return 0;
}

课本中和上述其实是一样的,一个用for,i=0,i=1来访问左右子树,弃用for,直接上

void maxLoading(int c1w,int t)
{if(t>=w.size()){if((wsum-c1w)<=c2){cout<<"choice "<<++s1<<":\n\tc1 :";for(int i=0;i<s.size();i++){if(s[i]==1){cout<<i<<"\t";}}cout<<endl;} } else{if(c1w+w[t]<=c1)//左子树{c1w+=w[t];s[t]=1;maxLoading(c1w,t+1);c1w-=w[t];}s[t]=0;maxLoading(c1w,t+1);//右子树}
}

B.迭代

其实就是二叉树遍历的非递归版本(直接爬的代码)

template <class Type>
Type MaxLoading(Type w[],Type c,int n,int bestx[])
{//迭代回溯法,返回最优装载量及其相应解,初始化根节点int i =1;int *x = new int[n+1];Type bestw = 0,cw = 0,r = 0;for(int j=1;j<=n;j++)r+=w[j];while(true){while(i<=n && cw+w[i]<=c){r -= w[i];cw +=w[i];x[i] =1;i++;}if(i>n){for(int j=1;j<=n;j++)bestx[j] = x[j];bestw = cw;}else{r -= w[i];x[i] = 0;i++;}while(cw+w[i] <= bestw){i--;while(i>0 && !x[i]){r+=w[i];i--;}if(i == 0){delete[] x;return bestw;}x[i] =0;cw -= w[i];i++;}}
}

2.批处理作业调度

排列树

#include<iostream>
#include<vector>
#include<cstdlib>
#include<climits>
#include<algorithm>
using namespace std;
struct Node
{int tm1;int tm2;
};
vector<int> x;//当前路径
vector<int> bestx;//最优路径
vector<int> f2;//机器2完成处理时间
vector<Node> v;//每个作业需要的处理时间 int bestT;
int f;//完成时间和
int f1;//机器1完成处理时间
int num;//作业数 int random(int start,int end)
{return start+rand()%(end-start);
}void flowShop(int t)//t>=1
{if(t>num){for(int i=1;i<=num;i++){bestx[i]=x[i];}bestT=f;}else{for(int i=t;i<=num;i++){f1+=v[x[i]].tm1;f2[t]=((f2[t-1]>f1)?f2[t-1]:f1)+v[x[i]].tm2; f+=f2[t];swap(x[t],x[i]); if(f<bestT)//剪枝剪去f>=bestT的分支 {flowShop(t+1);}swap(x[i],x[t]);f1-=v[x[i]].tm1;f-=f2[t];}}
}
int main()
{cin>>num;for(int i=0;i<=num;i++){Node t;t.tm1=random(1,10);t.tm2=random(1,10);v.push_back(t); x.push_back(i);//初始化路径为作业输入顺序 f2.push_back(0);bestx.push_back(-1);}f=0;f1=0;bestT=INT_MAX;flowShop(1);for(int i=1;i<=num;i++){cout<<bestx[i]<<"\t";}cout<<"\n"<<bestT<<endl;
}

3.符号三角形问题

行一的每个元素都具有两种选择(0,+,1,-),行n的符号由行n-1来决定。

子集树,未剪枝

#include<iostream>
#include<vector>
using namespace std;
vector<int> v;
int num;
int countSame=0;
bool isEqual()
{int count0=0;int k=num;for(int j=num;j>0;j--)//确定符号{for(int i=k;i<k+j-1;i++){v[i]=(v[i-j]==v[i-j+1])?0:1;}k+=j-1;}for(int i=0;i<(num+1)*num/2;i++)//统计减号个数{if(v[i]){count0++;}}if(count0*2==num*(num+1)/2)//是否相等{return true;}return false;
}
void Triangels(int t)
{if(t>=num){if(isEqual())//相等加1{countSame++;}}else{for(int i=0;i<=1;i++)//行一num个元素,均有两种选择{v[t]=i;Triangels(t+1);}}
}
int main()
{cin>>num;for(int i=0;i<(num+1)*num/2;i++){v.push_back(-1);}Triangels(0);cout<<countSame<<endl;
} 

4.n后问题

n后问题,解空间非子集树,非排列树(一直往这方向上靠,做了上面3个题,有些先入为主了)

每行皇后只有一位,用v[ i ] = j来表示皇后在( i , j ),确实没有想到(一直考虑用二维数组,或者一维数组模拟二维数组)

用斜率来判断是否在一条斜线上,这个也没想到

A.递归

#include<iostream>
#include<vector>
#include<cmath>
using namespace std;
vector<int> v;//v[i]=j,表示第i行,第j列是皇后
int num;//棋盘行长,列长,也是皇后个数
int sum;//可行方案个数
bool isOk(int t)
{for(int j=1;j<t;j++)//待选皇后v[t],是否与t行之前的皇后有冲突(每行一个皇后) {if(v[t]==v[j]||abs(t-j)==abs(v[t]-v[j]))//用斜率来判断是否在一条斜线上!!! {return false;}}return true;
}
void nQueen(int t)
{if(t>num){sum++;}else{for(int i=1;i<=num;i++){v[t]=i;//第t行的皇后放在第i列 if(isOk(t)){nQueen(t+1);}}}
}
int main()
{sum=0;cin>>num;for(int i=0;i<=num;i++){v.push_back(0);}nQueen(1);cout<<sum<<endl;
} 

B.迭代

#include<iostream>
#include<vector>
#include<cmath>
using namespace std;
vector<int> v;//v[i]=j,表示第i行,第j列是皇后
int num;//棋盘行长,列长,也是皇后个数
int sum;//可行方案个数
bool isOk(int t)
{for(int j=1;j<t;j++)//待选皇后v[t],是否与t行之前的皇后有冲突(每行一个皇后) {if(v[t]==v[j]||abs(t-j)==abs(v[t]-v[j]))//用斜率来判断是否在一条斜线上!!! {return false;}}return true;
}
void nQueen(int t)
{v[t]=0;while(t>0){v[t]+=1;//第t行,第v[t]+1列 while(!isOk(t)&&v[t]<=num)//列范围内,寻找符合条件的v[t]=j {v[t]+=1;}if(v[t]<=num)//找到 {if(t==num){sum++;//找到后,下个循环v[t]会大于num,有t--处理 }else{t++;v[t]=0;}}else//未找到 {t--;}}
}
int main()
{sum=0;cin>>num;for(int i=0;i<=num;i++){v.push_back(0);}nQueen(1);cout<<sum<<endl;
} 

5.0-1背包问题

子集树

#include<iostream>
#include<vector>
#include<cstdlib>
using namespace std;
struct Node
{int w;int p;
};
vector<Node> v;//物品
vector<int> x;//当前方案
vector<int> bestx;//存储最佳方案
int num;//物品数
int c;//背包容量
int maxP;//最大价值int random(int start,int end)
{return start+rand()%(end-start);
}
void storage(int cp)
{if(cp>maxP){maxP=cp;for(int i=0;i<num;i++){bestx[i]=x[i];}}
}
void knapsack(int cw,int t,int cp)
{if(t>=num){return;}else{if(cw<=c){for(int i=0;i<=1;i++){x[t]=i;if(i==1){cw+=v[t].w;cp+=v[t].p;}if(cw<=c){storage(cp);knapsack(cw,t+1,cp);   }}}}
}
int main()
{maxP=-1;cin>>num>>c;for(int i=0;i<num;i++){Node temp;temp.w=random(1,20);temp.p=random(1,100);v.push_back(temp);x.push_back(0);bestx.push_back(0);}   knapsack(0,0,0);cout<<maxP<<endl;
}

6.最大团问题

子集树

#include<iostream>
#include<vector>
using namespace std;vector< vector<int> > v;
vector<int> x;
vector<int> bestx;
int maxv;//最优解顶点个数
int num;void storage(int nowv)
{if(nowv>maxv){maxv=nowv;for(int i=0;i<num;i++){bestx[i]=x[i];}}
}
bool judge(int t)//是否都与t相连
{for(int i=0;i<t;i++){if(x[i]==1&&v[t][i]==0){ return false;}}return true;
}
void MaxClique(int t,int nowv)
{if(t>=num){storage(nowv);}else{for(int i=0;i<=1;i++){x[t]=i;if(i==0){MaxClique(t+1,nowv);}else if(i==1&&judge(t)){MaxClique(t+1,nowv+1); }}}
}int main()
{maxv=-1; cin>>num;//初始化全无边 for(int i=0;i<num;i++){vector<int> temp;for(int j=0;j<num;j++){temp.push_back(0);      }v.push_back(temp);x.push_back(-1);bestx.push_back(-1);}//输入边 while(true){int x1,y1;if(cin>>x1>>y1){v[x1][y1]=1;v[y1][x1]=1;}else{break;}}MaxClique(0,0);cout<<maxv<<endl;for(int i=0;i<num;i++){if(bestx[i]==1){cout<<i+1<<"\t";}}cout<<endl;
}

7.图的m着色问题

子集树,解空间子集树,不是非要往0-1二叉树上靠,也可以是m叉树

#include<iostream>
#include<vector>
using namespace std;vector< vector<int> > v;
vector<int> x;
int num;
int m;
int sum;bool judge(int t,int color)
{for(int i=0;i<t;i++){if(x[i]==color&&v[i][t]==1){return false;}} return true;
}
void coloring(int t)
{if(t>=num){cout<<sum<<": ";for(int i=0;i<num;i++){cout<<x[i]<<"\t";}cout<<endl;sum++;}   else{for(int i=0;i<m;i++){x[t]=i;if(judge(t,i)){coloring(t+1);}x[t]=0;}}
}int main()
{sum=0;cin>>num>>m;for(int i=0;i<num;i++){vector<int> temp;for(int j=0;j<num;j++){temp.push_back(0);}v.push_back(temp);x.push_back(-1);}while(true){int x1,y1;if(cin>>x1>>y1){v[x1][y1]=1;v[y1][x1]=1;}else{break;}}coloring(0);cout<<sum<<endl;
} 

8.旅行售货员问题

排列树

#include<iostream>
#include<vector>
#include<climits>
#include<algorithm>
#include<cstdlib>
using namespace std;vector< vector<int> > v;
vector<int> x;
int num;
int costbest;int random(int s,int e)
{return s+rand()%(e-s);
}
int countDis()
{int cost=0;for(int i=1;i<x.size();i++){cost+=v[x[i-1]][x[i]];}return cost+v[x[0]][x[x.size()-1]];
}
void Bttsp(int firstcity,int t)
{if(t>=num){int costx=countDis();if(costx<costbest){costbest=costx;}}else{for(int i=t;i<num;i++){if(x[0]==firstcity){swap(x[t],x[i]);Bttsp(firstcity,t+1);swap(x[t],x[i]);}}}
}
int main()
{costbest=INT_MAX;cin>>num;for(int i=0;i<num;i++){vector<int> temp;for(int j=0;j<num;j++){temp.push_back(0);}v.push_back(temp);x.push_back(i);}for(int i=0;i<num;i++){for(int j=i+1;j<num;j++){int temp=random(1,50);v[i][j]=temp;v[j][i]=temp;cout<<"v["<<i<<"]["<<j<<"]="<<temp<<endl;}}Bttsp(0,0);cout<<costbest<<endl;
} 

9.圆排列问题

排列树

#include<iostream>
#include<vector>
#include<cmath>
#include<climits>
#include<algorithm>
using namespace std;vector<int> x;
vector<int> r;
int num;
double disbest;double countDis(int a,int b)
{return sqrt(pow(a+b+0.0,2)-pow(a-b+0.0,2));
}
void circle(int t,double discost)
{if(t>=num){if(discost<disbest){disbest=discost;}}else{for(int i=t;i<num;i++){swap(x[t],x[i]);double temp;if(t+1==num){temp=r[x[t]];}else{temp=countDis(r[x[t]],r[x[t+1]]);}if(discost+temp<disbest){circle(t+1,discost+temp);}swap(x[i],x[t]);}}
}
void change()
{int temp=x[0];for(int i=1;i<num;i++){x[i-1]=x[i];}x[num-1]=temp;
}
int main()
{disbest=INT_MAX;cin>>num;for(int i=0;i<num;i++){int temp;cin>>temp;r.push_back(temp);x.push_back(i);}for(int i=0;i<num;i++){circle(0,r[x[0]]+0.0);change();} cout<<disbest<<endl;
}

回溯法(算法分析与设计)相关推荐

  1. 图的m色着色问题 回溯法(算法设计与分析)Java

    package demo1;public class Coloring {static int m;//颜色的数量static int[] x;//可行解static int n;//图的顶点个数st ...

  2. 五大经典算法之回溯法及其应用

    前言 回溯法是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的 ...

  3. 用c语言验证装载问题 回溯法,《算法分析与设计》期末考试复习题纲(完整版)...

    <算法分析与设计>期末复习题 一.选择题 1. 算法必须具备输入.输出和( D )等4个特性. A.可行性和安全 性 B .确定性和易读性 C.有穷性和安全 性 D .有穷性和确定性 2. ...

  4. 算法分析与设计课程复习之回溯法

    算法分析与设计课程复习之回溯法 一.基本思想 1.解空间 设问题的解向量为X=(x1,x2,-,xn) ,xi的取值范围为有穷集Si .把xi的所有可能取值组合,称为问题的解空间.每一个组合是问题的一 ...

  5. 算法分析与设计——回溯法实验报告

       算法导论  课程设计 成 绩 题    目:    回 溯 法 学院班级:        1613013         学    号:      16130130216       姓     ...

  6. C++——《算法分析与设计》实验报告——贪心算法与回溯法

    实验名称: 贪心算法与回溯法 实验地点: 实验目的: 1.理解贪心算法与回溯法的概念: 2.掌握贪心算法与回溯法的基本要素: 3.掌握贪心算法与回溯法的解题步骤与算法柜架: 4.通过应用范例学习贪心算 ...

  7. 算法分析与设计-八皇后问题(回溯法)

    回溯法: 回溯的意义是在递归直到可解的最小问题后,逐步返回原问题的过程,而这里所说的回溯算法实际上是一个类似枚举的搜索尝试方法,它的主题思想是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就 ...

  8. 算法设计与分析第5章 回溯法(二)【回溯法应用】

    第5章 回溯法 5.2 应用范例 1.0-1背包问题 有n件物品和一个容量为c的背包.第i件物品的重量是w[i],价值是p[i].求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和 ...

  9. 【算法分析】实验 4. 回溯法求解0-1背包等问题

    目录 实验内容 实验目的 实验结果 步骤1:描述与分析 步骤2:策略以及数据结构 步骤3 步骤4 步骤5 步骤6 实验总结 实验内容 本实验要求基于算法设计与分析的一般过程(即待求解问题的描述.算法设 ...

最新文章

  1. 在android平板上取位置和天气的实现方式
  2. react生命周期和组件生命周期
  3. Flutter开发之《头条 Flutter iOS 混合工程实践》笔记(54)
  4. ORM(一)OQL结构图
  5. 互联网思维之求职信,百战百胜
  6. 音视频出海,如何乘风破浪?
  7. 解决安装CMake报错:Could not find CMAKE_ROOT !!! CMake has most likely not been installed correctly.
  8. qml入门学习(二):引入js文件
  9. Java基础学习总结(72)——提升 java 代码的运行效率
  10. 【转】SSL/TLS协议运行机制的概述
  11. Vue修改mint-ui默认样式(默认风格)
  12. Java程序猿必须掌握的重点之一:Lock锁
  13. C++封装SQLite实例六
  14. 拓端tecdat|R语言网络分析友谊悖论案例
  15. 网易云音乐linux版_全线下架:网易云音乐难解的困境
  16. 计算机专业技能考核方案,巩义市计算机专业技能课教学考核方案.doc
  17. wakeup_sources
  18. Revealing ecosystem services relationships and their driving factors for five basins of Beijing(1)
  19. QGis二次开发:预览几何图形,QgsRubberBand的应用
  20. 你应该知道的 setTimeout 秘密

热门文章

  1. 全国计算机等级考试三级网络技术选择题考点
  2. 【电气设计】理论知识学习(持续更新中...)
  3. 对Xcode7真机调试的无力吐槽
  4. 基于PHP+MySQL的企业员工培训管理系统
  5. python网格交易_网格交易(期货)
  6. python 爬虫软件第一个程序
  7. fstream用法总结
  8. java加密常用的方法_java中常用接口对接加密方式
  9. LINUX下Socket编程 函数格式详解
  10. Java Web中Forward和redirect的区别