算法篇-8-回溯法-N皇后&最优装载&01背包
来源:互联网 发布:帆布包推荐 知乎 编辑:程序博客网 时间:2024/06/10 12:50
本系列所有代码https://github.com/YIWANFENG/Algorithm-github
回溯法思想
回溯法运行起来类似于遍历,只不过会在遍历过程中去除一部分不可能的无效遍历()。
解决的问题的答案一般可以由一个向量表示,例如V= {x1,x2,x3....},其中x1,x2,x3...的取值便为最优解。
解空间即该问题所有可能的解的集合,在表示上分为子集树与排列树。
子集树即时该问题的x1,x2,x3....的取值影响最优解,而排列数是该问题的x1,x2,x3....的排列影响最优解。
在问题求解过程中,可以遍历整个树求最优解,但是一般情况我们都会用限界函数与剪枝函数对搜索加以限制,来节省我们的时间。
(相关名词。活结点:孩子未遍历完的结点 扩展结点:正在生成孩子的结点,
死结点:孩子遍历完毕的结点)
N皇后的安排问题
算法分析思路以及相关数学公式:
设x[i] 表示皇后i放在棋盘的第i行的第X[j]列。
则可由题目规则可知,任意两个X[I]互不相同。并且2皇后不可放于同一斜线上。
那么可得约束条件2,( i –j) != (x[i] – x[j] ) 同一斜线上的行相减与列相减结果相等。
算法可为,从第一行遍历,检查该行的某处是否可以安排皇后,是的话则进行下一行的遍历查找。等到找到一个解向量后向前回溯一层继续寻找,直到寻找完所有的解向量。
程序源代码:
#include <iostream>#include <stdlib.h>using namespace std;class NQueenSolver {private: intn; //皇后数量 int*x; //每一行的皇后所在的列 intsum; //当前解的数量 private: boolValidate(int k) { //验证第k行皇后位置是否合理 for(int i=1; i<k; ++i) { if((k-i)==abs(x[i]-x[k])|| x[i]==x[k]) return false; } return true; } voidBacktrack(int t) { if(t>n) { //到达解空间树的叶子 ++sum; if(sum==1)show_plan(); } else { //遍历所有的子结点 for(inti=1; i<=n; ++i) { x[t] = i; if(Validate(t)) Backtrack(t+1); } } } voidBacktrack_Iterative() { int k =1 ; //处理层数 x[1] = 0; while(k>0) { ++x[k]; //遍历所有子结点,寻找满足约束的子结点 while(x[k]<=n&& !Validate(k)) ++x[k]; if(x[k]<=n){ //找到满足约束的子结点 if(k==n) { //到达叶子结点 ++sum; if(sum== 1) show_plan(); } else { //进入下一层结点 ++k; x[k]= 0; } }else { //所有子结点遍历完毕,回溯 --k; } } } voidshow_plan() { for(int i=1; i<=n; ++i) { for(intj=1; j<=n; ++j) { if(x[i]==j) cout<<"Q "; else cout<<"* "; } cout<<endl; } }public: intSolve(int num_queens) { //num_queens皇后数量 //解的数量 n = num_queens; sum = 0; x = new int[n+1]; Backtrack(1); delete []x; return sum; } intSolve_iterative(int num_queens) { //num_queens皇后数量 //解的数量 n = num_queens; sum = 0; x = new int[n+1]; Backtrack_Iterative(); delete []x; return sum; }}; int main(){ intnum = 0,sum; cout<<"输入皇后数\n"; cin>> num; NQueenSolverqs; sum= qs.Solve(num); //sum= qs.Solve_iterative(num); cout<<num<<"皇后问题解的数量:"<<sum<<endl; return0;}
//结果正确。由于解数量过多,在这里只输出了一组解。
最优装载问题
题目:
最优装载问题
求解一该问题解向量,使得该装载的最大重量最大。
算法分析思路以及相关数学公式:
子集树表示问题的解空间。
完全遍历解空间,每一次遍历到叶子结点,查看此时解是否比已知解更优,若更优则更新解。
限界函数1:当前选择可以装入,即当前解不超过额定最大载重量
限界函数2:选择当前解后可以存在最优解,即当前选择完后加上所有后面等待选择的解大于最优解(即才可能存在最优解)。
程序源代码:
#include <iostream>#include <stdlib.h>#include <stack>using namespace std;template <class T_>class MaxLoading {private:// in int n;const T_ *w; //每个集装箱重量 T_ c; //可载重量 // outT_ bestw;//当前最优载重量 int *bestx;//当前最优解//interiorint *x;//当前解T_ cw;//当前载重量 T_ r; //当前剩余物品重量 void Backtrack(int i) {if(i>n) {//到达叶子bestw = cw;for(int j=1; j<=n; ++j) bestx[j] = x[j]; return ;}//更新剩余重量T_ r_backup = r;r -= w[i];//搜索左子节点 if(cw+w[i]<=c) {//约束函数 x[i] = 1;T_ cw_backup = cw;cw+=w[i];Backtrack(i+1);cw = cw_backup; }//搜索右子结点if(cw+r>bestw) { //限界函数 x[i] = 0;Backtrack(i+1);} r = r_backup;} public:int Solve(int n_,T_ c_,const T_ *w_,int *bestx_) {//n_ 物品数量//c_ 最大载重量// w_[] 物品重量表// bestx_[] 最优解 n = n_;c = c_;w = w_;bestx = bestx_;x = new int[n+1];cw = 0;bestw = 0;r = 0;for(int i=1; i<=n; ++i) r+=w[i];Backtrack(1);delete [] x;return bestw;}};int main(){int n = 3,sum;MaxLoading<float> ml;float w[] = {0,10,40,40};int x[n+1];float bestw = ml.Solve(n,60.0,w,x);//float bestw = ml.Solve_Iterative(n,60.0,w,x);cout<<"最优值:"<<bestw<<endl<<"装载方式:\n";for(int i=1; i<=n; ++i) {cout<<x[i]<<' ';a}cin.get();return 0;}
0-1背包问题
算法分析思路以及相关数学公式:
此问题类似最优装载。在搜索子集树的解空间时,只要其做儿子结点是可行的一个结点,就进入左子树,当右子树可能包含最优解时才进入右子树,否则减去右子树。设r为当前剩余物品的价值总和,cp是当前价值,bestp是当前最优价值。当CP+r<=bestp时,可减去右子树。计算右子树中解的上界可以利用贪心选择法求背包问题的思想。
程序源代码:
#include <iostream>#include <stdlib.h>#include <algorithm> using namespace std; class Object {public: intID; //原始编号 floatd; //单位重量的价值 booloperator< (const Object & a) const { return d>=a.d; } }; template <class T_,class W_>class Knapsack {private: //in intn; T_*w; //每个物品重量 W_*p; //每个物品价值 ,需要从大到小排序 T_c; //背包可载重量 //out W_bestp; //当前最优载价值 int*bestx; //当前最优解 //interior int*x; //当前解 T_cw; //当前载重量 W_cp; //当前价值 private: W_Bound(int i) { //计算上界 T_ c_left = c- cw; W_ b = cp; while(i<=n &&w[i]<=c_left) { c_left-= w[i]; b+=p[i]; ++i; } if(i<=n) b+=p[i]*c_left/w[i]; return b; } voidBacktrack(int i) { if(i>n) { //到达叶子 bestp= cp; for(intj=1; j<=n; ++j) bestx[j] = x[j]; return; } //搜索左子节点 if(cw+w[i]<=c) {//约束函数 x[i]= 1; //T_cw_backup = cw; //W_cp_backup = cp; cw+=w[i]; cp+=p[i]; Backtrack(i+1); cw-=w[i]; cp-=p[i]; //cw= cw_backup; //cp= cp_backup; } //搜索右子结点 if(Bound(i+1)>bestp) { //限界函数 x[i]= 0; Backtrack(i+1); } } /*voidBacktrack_Iterative(int i) { // int k = 1; bool flag = true; while(k>0) { //左子树 if(flag&& cw+w[k]<=c) {//约束函数 x[k] = 1; T_ cw_backup = cw; cw+=w[i]; Backtrack(i+1); cw = cw_backup; } //右子树 if(flag&& cw+r>bestw) { //限界函数 x[i] = 0; Backtrack(i+1); } } }*/public: W_Solve(int n_,T_ c_,const T_ *w_,const W_ *p_,int *bestx_) { //n_ 物品数量 //c_ 背包最大载重量 // w_[] 物品重量表 // p_[] 物品价值 // bestx_[] 最优解 //返回最优价值 n = n_; c = c_; w = new T_[n+1]; p = new W_[n+1]; bestx = bestx_; x = new int[n+1]; cw = 0; cp = 0; bestp = 0; Object *Q = new Object[n]; T_ w_total = 0; //总重量 W_ p_total = 0; //总价值 for(int i=1; i<=n; i++) { Q[i-1].ID= i; Q[i-1].d= p_[i]/(float)w_[i]; ////!!! w_total+= w_[i]; p_total+= p_[i]; } //检查是否全可装下 if(w_total < c) { delete[] Q; for(inti=1; i<=n; ++i) x[i] = 1; returnp_total; } sort(Q,Q+n); //物品按单位价值从大到小排序 ,以便Bound() /*for(int i=0;i<n;++i) { cout<<Q[i].ID<<''<<Q[i].d<<endl; }*/ //一般情况 for(int i=1; i<=n; ++i) { p[i]= p_[ Q[i-1].ID ]; w[i]= w_[ Q[i-1].ID ]; } /*for(int i=1;i<=n;++i) { cout<<p[i]<<''<<w[i]<<endl; }*/ Backtrack(1); //最优解 for(int i=1; i<=n; ++i) x[Q[i-1].ID]= bestx[i]; for(int i=1; i<=n; ++i) bestx_[i]= x[i]; delete [] p; delete [] w; delete [] x; return bestp; } }; int main(){ intn = 4,sum; Knapsack<int,float>ml; intw[] = {0,3,5,2,1}; floatp[] = {0,9,10,7,4}; intx[n+1]; floatbestp = ml.Solve(n,7,w,p,x); cout<<"最优值:"<<bestp<<endl<<"装载方式:\n"; for(inti=1; i<=n; ++i) { cout<<x[i]<<' '; } cin.get(); return0;}
- 算法篇-8-回溯法-N皇后&最优装载&01背包
- 最优装载问题 回溯算法
- 回溯法:最优装载问题
- 算法-回溯法初探-n皇后问题
- N皇后问题 算法 回溯法
- 回溯算法之最优装载问题
- 回溯法-n皇后
- N皇后的回溯算法
- N 皇后求解回溯算法
- 回溯算法n皇后问题
- 回溯法解最优装载问题
- 回溯法解最优装载问题
- 回溯法最优装载问题(java)
- 【回溯法解决最优装载问题】
- 回溯法解决2n皇后(8皇后)问题
- 回溯算法-8皇后
- n皇后问题递归算法(回溯法)
- 回溯法实例―n皇后算法 (java实现)
- Java数据结构和算法-链表(4-双向链表)
- Linux下安装tar.gz类型的jdk,并配置环境变量
- 〖随记〗简单生活,开心就好~
- git添加忽略名单
- ASP.NET的Gridview控件简单使用(分页、序号、选框、全选)(附源码)
- 算法篇-8-回溯法-N皇后&最优装载&01背包
- 1006. Sign In and Sign Out (25)(-selfdone)
- Oracle触发器用法实例详解
- aop实现原理(自学)
- React开发环境配置
- zookeeper特性
- pva-faster-rcnn
- linux shell 算数运算.关系运算.布尔运算,字符串运算
- 测试fun