回溯法:输出n的全排列,解哈密顿问题,马的遍历问题,解八后问题
来源:互联网 发布:三国杀风火山林淘宝 编辑:程序博客网 时间:2024/05/17 13:05
回溯法:输出n的全排列
2012-07-15 20:09 by 有心故我在, 4 阅读, 0 评论,收藏,编辑1. 问题描述: 输出自然数1到n的所有不重复的排列,即n的全排列。
2. 问题分析:
(1) 解空间: n的全排列是一组n元一维向量(x1, x2, x3, ... , xn),搜索空间是:1<=xi<=n i=1,2,3,...,n
(2) 约束条件: xi互不相同。技巧:采用"数组记录状态信息", 设置n个元素的一维数组d,其中的n个元素用来记录数据1-n的使用情况,已经使用置0,未使用置1
#include<iostream>#include<memory>using namespace std;int count=0;//解个数int n;//输入数据int *a ;//解向量int *d ;//解状态void clear( ){ for(int i=1;i<=n+1;i++) { d[i]=0; }}void output()//第0个不存{ for(int i=1;i<=n;i++) cout<<a[i]<<ends; cout<<endl;}void tryArrangement(int k){ for(int i=1;i<=n;i++) { if(d[i]==0) { a[k]=i; d[i]=1; } else { //表明已用过 continue; } if(k<n) //没搜索到底 { tryArrangement(k+1); } else { count++; output(); } d[a[k]]=0; //回溯 } }int main(){ cin>>n; a=new int[n+5];//解向量 d=new int[n+5];//解状态 clear( ); tryArrangement(1); }
最开始是这样置d[ ]为0的:
memset(d,0,sizeof(d));
不行,为什么?
记住,memset是按字节赋值的,第3个参数指定要赋值的字节的大小,应该改成:
memset(d,0,sizeof(int)*n);
算法说明:k为当前处理的第k个元素。上面的复杂度为O(n^n),不是一个好的算法,因此不可能用它去搜索排序树。
调试分析
k=1 if(d[1]==0) true a[1]=1; d[1]=1 try(2)
k=2 if(d[2]==0) a[2]=2 d[2]=1 try(3)
k=3 if(d[3]==0) a[3]=3; d[3]=1;
3==n; count=1; output 1 2 3
d[3]=0;
} 跳到for( )处运行 F11运行都函数末尾,
F11又跳到try(3) k=2 d[a[2]]=0
} 跳到For处运行
if(d[3]==0) a[2]=3 d[3]=1;
if(k<3) try(3)
另一种解法==也是搜索排列树的算法框架。
设计:根据全排列的概念,定义数组初始值为(1,2,3,4,。。n),这是全排列的一种结果,然后通过数据间的交换,则可产生所有不同的排列:
#include<iostream>using namespace std;int a[100],n,s=0;void output(){ for(int j=1;j<=n;j++) cout<<a[j]<<ends; cout<<endl;}void tryArrange(int k){ int j; if(k>n) { output(); } else for(j=k;j<=n;j++) { swap(k,j); tryArrange(k+1); swap(k,j); //回溯时,恢复原来的排列 }}int main(){ int i; cin>>n; for(i=1;i<=n;i++) a[i]=i; tryArrange(1); cout<<"换行符"<<"s="<<s;}
注意c++try是关键字。swap在ios文件中。
回溯法解哈密顿问题
2012-07-12 17:46 by 有心故我在, 8 阅读, 0 评论,收藏,编辑回溯法求解:
首先把回路中的所有顶点编号初始化为0,然后,把顶点0当做回路中的第0个顶点,即x0=0;搜索与0邻接的编号最小的顶点,作为它的后续顶点,判断是否满足约束条件,是则到达该顶点,x1取值为满足条件的顶点编号,然后再以同样的方式搜索。
假设在搜索过程中已经生成了通路L=x0x1....,xi-1,在继续搜索某个顶点作为通路的xi顶点时,
根据约束条件,在V中选择与xi-1邻接的并且不属于L的编号最小的顶点。
如果搜索成功,则把该顶点作为xi,否则就把L中的xi-1删去(回溯),从xi-1顶点编号加1的位置开始,继续搜索与xi-2相邻接且不属于L的顶点。这个过程一直继续下去。
当搜索到xn-1时,如果xn-1与x0相邻接,就得到了一天hamilton回路,否则把xi-1删去,继续回溯。
最后,在回溯过程中,L中只剩下一个顶点x0,表明不存在哈密顿回路。
#include<iostream>#include<cstdio> using namespace std; void hamilton(int **a,int n,int x[])//x[] 存放回路的顶点序号 a不能写成二维数组形式,否则报错:不能从int **转为 int [ ]]{ bool *s=new bool[n]; //s记录顶点的使用与否 for(int i=0;i<n;i++) { x[i]=-1; s[i]=false; } s[0]=true; x[0]=0; //从序号0开始搜索 int k=1; //初始深度为1,因为有n个节点且第一个节点已给出(k=0),故空间搜索树的深度为n-1(1到n-1) while(k>=0) { x[k]=x[k]+1; while(x[k]<n) { if((!s[x[k]])&&(a[x[k-1]][x[k]]==1)) { //顶点x[k]未被使用而起与前一节点x[k-1]有连线 break; } else x[k]=x[k]+1; } if(x[k]<n) { if(k!=n-1) //搜索成功,深度加1 { s[x[k]]=true; k=k+1; } else break; } else { x[k]=-1; k=k-1; s[x[k]]=false; } }} int main(){ int n; freopen("货郎担问题42.txt","r",stdin); cin>>n; //顶点数 int **a=new int *[n]; for(int i=0;i<n;i++) a[i]=new int[n]; for(int i=0;i<n;i++) for(int j=0;j<n;j++) cin>>a[i][j]; int *x=new int[n]; hamilton(a,n,x); for(int i=0;i<n;i++) cout<<x[i]<<ends; cout<<endl;}
文件如下:
4
0 0 1 1
1 0 1 1
1 1 0 1
输出0 2 1 3
回溯法解马的遍历问题
2012-06-08 15:36 by 有心故我在, 9 阅读, 0 评论,收藏,编辑马的遍历问题:在n*m的棋盘上,马只能走日字。马从位置(x,y)处出发,把棋盘的每一点都走一次,且只走一次,找出所有路径。
问题分析:行n,列m,马在不出边界的情况下有8个方向可以行走(走日字),如当前坐标为(x,y),则行走后的坐标可以为:
(x+1,y+2) (x+1,y-2), (x+2,y+1) (x+2,y-1)
(x-1,y-2) (x-1,y+2) (x-2,y-1) (x-2,y+1)
回溯法算法设计: 搜索空间是n*M个点,约束条件是不出边界且每个点只经过一次,节点的扩展规则如上所述。
搜索过程是从任一点(x,y)出发,按深度优先原则,从8个方向尝试一个可以走的点,直到走过n*m个点。用递归容易实现。
注意:问题要求找出所有可能的解。就要注意回溯过程的清理现场工作,就是置当前位置为未经过。
数据结构设计:用一个变量dep记录递归深度,也就是走过的点数。当dep=n*m;找到一组解。
用n*m的二维数组记录马走过的过程,初始值为0表示未经过,起点存储的是1,终点存储的是n*m。
#include<iostream>using namespace std;void output();int n=5,m=4;int fx[8]={1,2,2,1,-1,-2,-2,-1};int fy[8]={2,1,-1,-2,-2,-1,1,2};int a[5][4]; //下标从1开始int dep,x,y,count; bool check(int x,int y){ if(x>=1&&x<=n&&y>=1&&y<=m&&(!a[x][y])) return true; else return false;} void find(int x,int y,int dep){ int i,xx,yy; for(i=1;i<=8;i++) //加上方向增量,形成新的坐标 { xx=x+fx[i]; yy=y+fy[i]; if(check(xx,yy)) //判断新坐标是否出界,是否已走过 { a[xx][yy]=dep; if(dep==n*m) output(); else find(xx,yy,dep+1); a[xx][yy]=0; //回溯,恢复未走未走 } }}void output(){ count++; cout<<"\n"; cout<<"count="<<count; for(y=1;y<=n;y++) { cout<<endl; for(x=1;x<=m;x++) cout<<a[y][x]<<ends; }}int main(){ int i,j; count=0; dep=1; cout<<"please input x,y"; cin>>x>>y; if(x>n||y>m||x<1||y<1) { cout<<"input error"; return -1; } for(i=1;i<=n;i++) for(j=1;j<=m;j++) a[i][j]=0; a[x][y]=1; find(x,y,2); if(count==0) cout<<"no solution!"; else cout<<"nambers of solution="<<count<<endl;}
代码输出好像有错误。
回溯法解八后问题
2012-05-20 22:58 by 有心故我在, 19 阅读, 0 评论,收藏,编辑在一个8×8国际象棋盘上,有8个皇后,每个皇后占一格;要求皇后间不会出现相互“攻击”的现象,即不能有两个皇后处在同一行、同一列或同一对角线上。问共有多少种不同的方法。
我们用回溯法,现在的目的不是找有多少种解法,而是只要找出一种合适的解法输出即可。
先写一个place函数,判断当前位置是否合法:
bool place(int x[],int k){ int i; for(i=1;i<k;i++) if((x[i]==x[k])||(abs(x[i]-x[k])==abs(i-k))) return false; return true;}
这个函数以解向量x[]和皇后的行号k做参数,判断第k个皇后当前的列位置x[k]是否满足关系式,这样,他必须和第1~k-1行的所有皇后位置进行比较。
n皇后算法如下:
/*n 后问题输入:皇后个数n输出: n后问题的解向量*/void n_queens(int n,int x[]) { int k=1; //x[0]不要 x[1]=0; while(k>0) { x[k]=x[k]+1; //在当前列加1的位置开始搜索 while(x[k]<=n&&(!place(x,k))) //当前列是否满足条件 x[k]=x[k]+1; if(x[k]<=n) //存在满足条件的列 { if(k==n) break; //是最后一个皇后,完成搜索 else { k=k+1; x[k]=0; //不是,处理下一个皇后 } } else //已判断完n列,均没有满足条件 { x[k]=0; k=k-1; //第k行复位为0,回溯到前一行 ,前一行列加1 x[k]=x[k]+1 } }//end while}
main函数如下:
int main(){ int n; cout<<"请输入皇后的数"; cin>>n; int *a=new int[n+2]; n_queens(n,a); cout<<"解向量为"<<endl; for(int i=1;i<=n;i++) cout<<a[i]<<ends; cout<<endl; for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(j==a[i]) cout<<"* "<<ends; //要在*后留一个空格,口字占2格 else cout<<"口"<<ends; } cout<<endl; }}
用递归求解有:
解向量 (x1,x2,......xn)
显约束 xi=1,2.....n
隐约束:
1)不同列 xi!=xj
2)不出来同一正,反对角线 |i-j|!=|xi-xj|
#include<iostream>#include<cstdlib>using namespace std;#define NumQueen 8int queen[NumQueen];int sum=0; //解决方案总数 8后有92组解void display(){ int i,j; cout<<"第"<<sum+1<<"个解决方案-->"; for(i=0;i<NumQueen;i++) { for(j=0;j<NumQueen;j++) if(queen[i]==j) cout<<"("<<i+1<<","<<queen[i]+1<<")"; } cout<<endl; sum++;//解的组数}bool check(int k){ int i; for(i=0;i<k;i++) if((queen[i]==queen[k])||(abs(queen[i]-queen[k])==abs(i-k))) return false; return true;} void putQueen(int k) { int i; for(i=0;i<NumQueen;i++) { queen[k]=i; if(check(k)) { if(k==NumQueen-1) display(); else putQueen(k+1); } } } int main() { cout<<"方按,其中(行标,列标)为皇后的位置\n\n"; putQueen(0); cout<<"\n共有"<<sum<<"个方案\n"; }
- 回溯法:输出n的全排列,解哈密顿问题,马的遍历问题,解八后问题
- 回溯——哈密顿回路问题
- n皇后问题的两种方法:常规的回溯法和利用全排列的回溯法
- 输出N个字母的全排列问题
- 回溯-全排列问题
- n个数的排列问题(回溯法)
- 哈密顿路问题
- 哈密顿路问题
- HDOJ 2181 哈密顿绕行世界问题 (回溯)
- HDU 2181 哈密顿绕行世界问题 (求一个图的所有哈密顿回路)
- 算法学习---关于哈密顿图的哈密顿通路求解问题
- 回溯法求解全排列问题
- 全排列问题(回溯求解)
- N皇后问题(回溯VS全排列)
- 输出全排列问题
- 回溯法搜索排列树的问题
- 全排列的问题
- 全排列与回溯法解旅行商问题
- Django REST/ Django RESTful
- jquery控制css绝对定位位置效果,例如鼠标移动到图标显示层显示相关信息
- Apache commons (Java常用工具包)简介
- 回溯法简介
- C++ 读取组策略中的密码策略
- 回溯法:输出n的全排列,解哈密顿问题,马的遍历问题,解八后问题
- sizeof详解
- N9 安装Qt5
- Ubuntu下Jenkins 安装
- Huffman编码树
- java位运算
- Android4.0 设置系统默认输入法
- 在虚拟机vmware8.0上安装mac os lion 10.7和xcode4.1
- ZOJ 3633 Alice's present