搜索与回溯

                        搜索与回溯


 

回溯算法

回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。 
 
  简言之就是先按照一条路走,走到不能再往前时,后退一步,选择另一条道路,如果
仍是没有路,就再退一步,直到将所有路都探索完。
 
例:在有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 大小的。

输出格式

前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。

输入输出样例

输入 #1
6
输出 #1
2 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 }

 

深搜优化技巧

posted @ 2021-02-05 18:42  S_Curry  阅读(313)  评论(0编辑  收藏  举报