经典算法学习之回溯法

回溯法的应用范围:只要能把待求解的问题分成不太多的步骤,每个步骤又只有不太多的选择就可以考虑使用回溯法。

若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束。 而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束。

回溯法将问题的候选解按照某一顺序逐一枚举和检验。当发现当前候选解不可能是解时,就选择下一个候选解,若当前候选解符合要求,且还未达到求解的规模,则继续扩展当前候

选解的规模,如果候选解已经满足了所有要求,并且也达到了问题的规模,那么该候选解就是问题的一个解。

例、在9(3*3)的方格内填入1到N(N>=10)内的某9个数字,每个方格填入一个数字,使方格内所有相邻的两个数的和为质数,试求出满足要求的所有填法。

伪代码:

 1 int m=0;       //已经填入m个数
 2 bool ok=true;//ture表示已经填入的前m个数都符合要求
 3 int n=8;      //填入最后一个数字的下标
 4 do
 5 {
 6     if(ok)
 7     {
 8         if(m==n)
 9         {
10             print();//输出这个解
11             change();//调整最后一个值,找下一个解
12         }
13         else
14         {
15             extend();//往下一个空的方格中填入数字
16         }
17     }
18     else
19     {
20         change();//调整最近填入的一个值
21     }
22     ok=检查前m个数是否都符合要求;
23 }while(m>=0)//由于是求解所有解,所以必须回溯到根的所有情况才算结束

程序如下:

  1 #include<iostream>
  2 #include<math.h>
  3 using namespace std;
  4 const int N=12;
  5 int a[10];//记录符合条件的数
  6 bool b[N+1];//记录某个数是否已经被用过true,被用过,false,没被用过
  7 int checkmatrix[][3]={{-1},{0,-1},{1,-1},
  8                       {0,-1},{3,1,-1},{4,2,-1},
  9                       {3,-1},{6,4,-1},{7,5,-1}};//checkmatirx用来验证相邻的两个数的和是否为指数用的,-1是人为添加的截止符号
 10 int m;//已经符合条件的个数
 11 void print(int * a)
 12 {
 13     for(int i=0;i<3;i++)
 14     {
 15         for(int j=0;j<3;j++)
 16         {
 17             cout<<a[i+j*3]<<" ";
 18         }
 19         cout<<endl;
 20     }
 21 }
 22 bool isprime(int m)
 23 {
 24     if(1==m)
 25     {
 26         return false;
 27     }
 28     if(2==m)
 29     {
 30         return true;
 31     }
 32     int n=sqrt((float)m);
 33     for(int i=2;i<=n;i++)
 34     {
 35         if(m%i==0)
 36         {
 37             return false;
 38         }
 39     }
 40     return true;
 41 }
 42 //返回从start开始的第一个没被用过的数
 43 int select(int start)
 44 {
 45     for(int i=start;i<=N;i++)
 46     {
 47         if(b[i]==false)
 48         {
 49             return i;
 50         }
 51     }
 52     return 0;//如果全都被使用过了就返回0
 53 }
 54 int extend(int m)
 55 {
 56     a[++m]=select(1);
 57     b[a[m]]=true;
 58     return m;
 59 }
 60 int change(int m)
 61 {
 62     int pos;
 63     while(m>=0 && (pos=select(a[m]+1))==0)
 64     {
 65         b[a[m--]]=false;
 66     }
 67     if(m<0)
 68     {
 69         return -1;
 70     }
 71     else
 72     {
 73         b[a[m]]=false;
 74         a[m]=pos;
 75         b[a[m]]=true;
 76     }
 77     return m;
 78 }
 79 bool check(int m)
 80 {
 81     int j;
 82     if(m<0)
 83     {
 84         return false;
 85     }
 86     int k=m;
 87     while(k>=0)
 88     {
 89         for(int i=0;(j=checkmatrix[k][i])>=0;i++)
 90         {
 91             if(!isprime(a[k]+j))
 92             {
 93                 return false;
 94             }
 95         }
 96         k--;
 97     }
 98
 99     return true;
100 }
101 int main()
102 {
103     //先全都初始化为未使用过
104     for(int i=0;i<N+1;i++)
105     {
106         b[i]=false;
107     }
108     m=0;
109     a[m]=1;
110     b[a[m]]=true;
111     bool ok=true;//前m个数都满足条件的话ok为true否则为false
112     do
113     {
114         if(ok)
115         {
116             if(8==m)
117             {
118                 print(a);
119                 //调整
120                 m=change(m);
121
122             }
123             else
124             {
125                 //扩展
126                 m=extend(m);
127             }
128         }
129         else
130         {
131             //调整
132             m=change(m);
133         }
134         //检查前m个数是否都满足条件
135         ok=check(m);
136     }while(m>=0);
137
138     return 0;
139 }

如果将题目改成找出一种填法,则不需要回溯到根,而是找到一个解就可以结束了
伪代码如下:

 1 int m=0;
 2 bool ok=true;
 3 int n=8;
 4 do
 5 {
 6     if(ok)
 7     {
 8          extend()//扩展解
 9     }
10     else
11     {
12          change();//调整最近填入的数字
13     }
14     ok=检查填入的m个数的合理性;
15 }while((m!=n||!ok)&&m!=0)
16 if(m==n)
17 {
18     print();//输出解
19 }
20 else
21 {
22     无解;
23 }
时间: 2024-10-14 06:34:40

经典算法学习之回溯法的相关文章

算法学习笔记——回溯法

一.基本概念 回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就"回溯"返回,尝试别的路径. 回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为"回溯点". 许多复杂的,规模较大的问题都可以使用回溯法,有"通用解题方法"的美称. 二.基本思想 在包

经典算法学习——非循环双向链表实现冒泡排序(不带头结点)

我在前面两篇博客<经典算法学习--单链表(不带头结点)实现冒泡排序><经典算法学习--单链表实现冒泡排序(带头结点)>中详细描述了分别使用带头结点和不带头结点的单链表实现了冒泡排序,让我们对单链表和冒泡排序有了理性的认识.今天我们将会来使用不带头结点的非循环双向链表来实现冒泡排序,在处理过程中,这种冒泡比前面两种更为简单高效.代码上传至 https://github.com/chenyufeng1991/DoubleLinkedList_BubbleSort . 核心代码如下: /

经典算法学习——链表实现冒泡排序

我在之前一篇博客<经典算法学习--冒泡排序>中简单实现了使用数组进行冒泡排序.这篇博客我们将来实现使用链表如何排序,其实整体的思路是一样的.示例代码上传至: https://github.com/chenyufeng1991/BubbleSortLinkedList . 算法描述如下: (1)比较相邻的前后两个数据,如果前面数据大于后面的数据,就将两个数据交换: (2)这样对数组的第0个数据到N-1个数据进行一次遍历后,最大的一个数据就到了最后一个位置,也就是下标为N-1的位置(沉到了水底).

(转)五大常用算法之四:回溯法

http://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741376.html 1.概念 回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径. 回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”. 许多复杂的,

五大常用算法之四:回溯法

(转自:http://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741376.html) 1.概念 回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径. 回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”. 许

算法思想之回溯法

一.概念 回溯:当把问题分成若干步骤并递归求解时,如果当期步骤没有合法选择,则函数将返回上一级递归调用,这种现象称为回溯. 回溯算法应用范围:只要把待求解问题分成不太多的步骤,每个步骤又只有不太多的选择,即可以考虑用回溯法. 回溯算法实际上是一个递归枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径. 回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不

Javascript经典算法学习1:产生随机数组的辅助类

辅助类 在几个经典排序算法学习部分,为方便统一测试不同算法,新建了一个辅助类,主要功能为:产生指定长度的随机数组,提供打印输出数组,交换两个元素等功能,代码如下: function ArraySortUtility(numOfElements) { this.dataArr = []; this.pos = 0; this.numOfElements = numOfElements; this.insert = insert; this.toString = toString; this.cle

算法java实现--回溯法--图的m着色问题

(转自:http://blog.csdn.net/lican19911221/article/details/26264471) 图的m着色问题的Java实现(回溯法) 具体问题描述以及C/C++实现参见网址 http://blog.csdn.NET/lican19911221/article/details/26228345 /** * 着色问题 * @author Lican * */ public class Coloring { int n;//图的顶点数 int m;//可用颜色数 i

算法学步:回溯法-8皇后问题

问题描述: 有八个皇后(可以当成八个棋子),如何在 8*8 的棋盘中放置八个皇后,使得任意两个皇后都不在同一条横线.纵线或者斜线上 做法: 从第一行开始,一行一行地考虑,这样起码可以保证皇后不在同一行: 考虑下面的每一行的时候,需要让新增加的棋子不在前面添加的棋子的左下.正下.右下,即新增加的棋子的列不等于前面棋子的列,它的行号+列号不等于前面棋子的行号加列号(不在左下),|行号-列号|不等于之前棋子的|行号-列号|(不在右下): 如果该行所有位置都不符合要求,则回溯到前一行,改变皇后的位置,继