搜索与回溯
搜索与回溯
回溯算法
回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。
简言之就是先按照一条路走,走到不能再往前时,后退一步,选择另一条道路,如果
仍是没有路,就再退一步,直到将所有路都探索完。
例:在有n个整数的集合{1,2,3,4,...,n}中选取r(r<n)数进行排列,是列出所有排列。
有些同学可能想到了排列组合A(n,r)。但注意题目,要求列出所有排列。
我们只能老老实实地一个个枚举。
看代码
#include<iostream> using namespace std; int a[10000000],n,r,num=0; bool b[1000000]={}; //用bool型变量判断该数是否可用 int search(int); int print(); int main() { cin>>n>>r; search(1); cout<<num; } int search(int k) { int i; for(i=1;i<=n;i++) //从1开始搜索 if(!b[i]) //如果没有用过,则执行下面程序 { a[k]=i; //将i赋值给a[k]; b[i]=1; if(k==r) //如果够了r个数,输出 print(); else search(k+1);//不够的话继续选取 b[i]=0; //退后一步 } } int print() { num++; for(int j=1;j<=r;j++) cout<<a[j]<<" "; cout<<endl; }
举个例子,5个数选3个排列,先选了1①,不够r,又开始递归search,for②循环中,发现1用过了,又跳到2,
没用过,还是不够r,又递归,for③1,2不能用选取了3,此时够了r,输出,将3再初始化为0,此时for③循环进行到了4,输出,初始化,又到了5,这循环结束之后,for②进行到了3,以此类推,直到①循环完了5,结束。
①②为第几层循环。
P1219 [USACO1.5]八皇后 Checker Challenge
题目描述
一个如下的 6 ×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
上面的布局可以用序列 2\ 4\ 6\ 1\ 3\ 52 4 6 1 3 5 来描述,第 ii 个数字表示在第 ii 行的相应位置有一个棋子,如下:
行号 1\ 2\ 3\ 4\ 5\ 61 2 3 4 5 6
列号 2\ 4\ 6\ 1\ 3\ 52 4 6 1 3 5
这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 33 个解。最后一行是解的总个数。输入格式
一行一个正整数 n,表示棋盘是 n ×n 大小的。
输出格式
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。
输入输出样例
输入 #16输出 #12 4 6 1 3 5 3 6 2 5 1 4 4 1 5 2 6 3 4说明/提示
【数据范围】
对于 100% 的数据,6<=n<=13
这就是经典的八皇后问题。首先分析题,每行只能放一个,那么我们可以用一维数组
来表示位置,a[5]=3,就可以表示第五行,第三列。
对角线分为两条,“/”与“\”,前者在同一对角线的规律为x-y值相同,后者x+y值相同。
故我们可以用bool型变量来表示这一对角线是否可用。
直接来看代码
1 #include<iostream> 2 using namespace std; 3 int a[15]; 4 int n,tot=0; 5 bool b[100],c[100],d[100]; 6 int print(int t) //输出函数 7 { 8 tot++; 9 if(tot<=3) 10 { 11 for(int i=1;i<=n;i++) 12 cout<<a[i]<<" "; 13 cout<<endl; 14 } 15 } 16 int search(int t) //t为摆放第几个棋子 17 { 18 for(int j=1;j<=n;j++) 19 if((!b[j])&&(!c[j-t+14])&&(!d[t+j])) //判断是否可用 (!b[j])判断该列 ,(!c[j-t+14]) 20 { //判断“/”对角线,由于可能会出现负数,我们加上14. 21 a[t]=j; 22 b[j]=1;c[j-t+14]=1;d[t+j]=1; 23 if(t==n) //如果放置够了,则输出。 24 print(t); 25 else search(t+1); //不够的话,继续放下一个 26 b[j]=0;c[j-t+14]=0;d[t+j]=0; 27 } 28 } 29 int main() 30 { 31 cin>>n; 32 search(1); 33 cout<<tot; 34 return 0; //好习惯不能忘。 35 }