回溯算法——>8皇后问题

一、算法的描述

回溯算法实际是一个类似枚举的搜索尝试方法,它的主题思想是在搜索尝试中找问题的解,当不满足求解条件就”回溯”返回,尝试别的路径。回溯算法是尝试搜索算法中最为基本的一种算法,其采用了一种“走不通就掉头”的思想,作为其控制结构。

二、算法的设计思想

回溯法是在包含问题的所有解的解空间树中。按照深度优先的策略,从根结点出发搜索解空间树,算法搜索至解空间树的任一结点时,总是先判断该结点是否满足问题的约束条件。如果满足进入该子树,继续按深度优先的策略进行搜索。否则,不去搜索以该结点为根的子树,而是逐层向其祖先结点回溯。
回溯法就是对隐式图的深度优先搜索算法。
1)确定问题的解空间
问题的解空间应只至少包含问题的一个解。
2)确定结点的扩展规则
如每个皇后在一行中的不同位置移动,而象棋中的马只能走“日”字等。
3)搜索解空间
回溯算法从开始结点出发,以深度优先的方式搜索整个解空间。这个开始结点就成为一个活结点,同时也成为当前的扩展结点。在当前的扩展结点处,搜索向纵深方向移至一个新结点。这个新结点就成为一个新的活结点,并成为当前扩展结点。如果在当前的扩展结点处不能再向纵深方向移动,则当前扩展结点就成为死结点。此时,应往回移动至最近的一个活结点处,并使这个活结点成为当前的扩展结点。回溯法即以这种工作方式递归地在解空间中搜索,直至找到所要求的解或解空间中已没有活结点时为止。

三、算法的适用场景

8皇后问题、n皇后问题、马踏棋盘问题、走迷宫问题、排列及排列树的搜索问题及最优化问题的回溯搜索等等。

四、算法框架

1)问题框架

设问题的解是一个n维向量(a1,a2,……,an),约束条件是ai(i=1,2,3……n)之间满足某种条件,记为f(ai)

2)非递归回溯框架

int a[n],i;
初始化数组a[ ];
i=1;
While (i>0(有路可走)) and ([未达到目标]) /还未回溯到头/
 {if (i>n)         /正在处理第i个元素/
     搜索到一个解,输出;
  else
   {a[i]第一个可能的值;
    while (a[i]在不满足约束条件  且  在在搜索空间内)   
       a[i]下一个可能的值;
    if (a[i]在搜索空间内)       
      {标识占用的资源; i=i+1;}    /扩展下一个结点/
    else  {清理所占的状态空间;  i=i-1;}/回溯/
  }}

3)递归算法框架

一般情况下用递归函数来实现回溯法比较简单,其中i为搜索深度。

int a[n];
try(int i)
{
    if (i>n)
        输出结果;
    else 
        for( j=下界; j<=上界;j++/枚举i所有可能的路径/
        { iff(j)/满足限界函数和约束条件/
            { a[i]=j;
                 ……            /其它操作/
              try(i+ 1);
            }
        }
  回溯前的清理工作(如a[i]置空值等)}

五、典型例题一:8皇后问题

1.一般解法:穷举

queen1(  )
{
int a[9];
for (a[1]=1;a[1]<=8;a[1]++)     
  for (a[2]=1;a[2]<=8;a[2]++)  
   {ifcheck(a,2)=0continue;
    for (a[3]=1;a[3]<=8;a[3]++)     
      {ifcheck(a,3)=0continue;
         for (a[4]=1;a[4]<=8;a[4]++)
          {ifcheck(a,4)=0continue;
            for (a[5]=1;a[5]<=8;a[5]++)
             {ifcheck(a,5)=0continue;
               for (a[6]=1;a[6]<=8;a[6]++)  
                 {ifcheck(a,6)=0continue;
                    for(a[7]=1;a[7]<=8;a[7]++)   
                    {ifcheck(a,7)=0continue;
                         for(a[8]=1;a[8]<=8;a[8]++)
                        {ifcheck(a,8)=0continue;
                        else  
                            for(i=1;i<=8;i++)
                                print(a[i]);
                         }
                    }
                } 
            } 
        } 
     }  
  } 
}
check*(a[],n)
{
    int i,j;
    for(i=2;i<=n;i++)
        for(j=1;j<=i-1;j++) 
            if(abs(a[i]-a[j])=abs(i-j)or(a[i]=a[j]))
                return(0);
 return(1);
}    

2.非递归回溯

int a[20],n; 
queen2(  )
{ 
    input(n);
    backdate(n);    
}
backdate (int n)
{ 
    int k;
    a[1]=0;       
    k=1;
    while( k>0 )
    {
        a[k]=a[k]+1;
        while ((a[k]<=n) and (check(k)=0)) /搜索第k个皇后位置/
          a[k]=a[k]+1;   
        if( a[k]<=n)
            if(k=n ) output(n);     / 找到一组解/
          else 
            {
                k=k+1; 继续为第k+1个皇后找到位置/
                a[k]=0;
            }/注意下一个皇后一定要从头开始搜索/
         else  
             k=k-1;           /回溯/
    }
}
check(int k)
{ 
    int i;
    for(i=1;i<=k-1;i++)
        if (abs(a[i]-a[k])=abs(i-k) or  (a[i]=a[k]))
            return(0);
        return(1);
}
output( )
{ 
    int i;
    for(i=1;i<=n;i++)
        print(a[i]);
}

3.递归回溯

int a[20],b[20],c[40],d[40];
int n,t,i,j,k;      /t记录解的个数/
queen3(  )
{ 
    int i,
    input(n);
    for(i=1;i<=n;i++)
    { 
        b[i]=0;
        c[i]=0; 
        c[n+i]=0;
        d[i]=0; 
        d[n+i]=0;
    }
    try(1;
}
try(int i)
{
    int j;
    for(j=1;j<=n;j++)      /第i个皇后有n种可能位置/   
        if (b[j]=0) and (c[i+j]=0) and (d[i-j+n]=0)
        {
            a[i]=j;           /摆放皇后/
            b[j]=1;           /占领第j列/     
            c[i+j]=1;
            d[i-j+n]=1;   /占领两个对角线/
            if (i<n) 
                try(i+1); /n个皇后没有摆完,递归摆放下一皇后/
            else 
                output( );        /完成任务,打印结果/
            b[j]=0; 
            c[i+j]=0;  
            d[i-j+n]=0; /回溯/
            }     
}
output( )
{  
    t=t+1;
    print(t,'       ');
    for( k=1;k<=n;k++)
           print(a[k],'   ');
    print(“ 换行符”);
}

递归算法的回溯是由函数调用结束自动完成的,也不需要指出回溯点,但也需要“清理现场”——将当前点占用的位置释放,也就是算法try()中的后三个赋值语句。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值