• 经典算法学习之回溯法


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

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

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

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

     例、在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 }
  • 相关阅读:
    Win64 驱动内核编程-12.回调监控进线程创建和退出
    我的主站 SHARELIST -分享列表 (功能持续完善中 2019-11-24 版本0.3)
    Caddy-基于go的微型serve用来做反向代理和Gateway
    Docker Swarm删除节点
    Docker Swarm集群搭建
    Docker Machine-Windows
    Docker Compose
    Docker网络配置进阶
    Docker网络相关
    Docker数据管理
  • 原文地址:https://www.cnblogs.com/bewolf/p/4390985.html
Copyright © 2020-2023  润新知