算法——回溯法

回溯法

1.概述

回溯(backtracking)法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

回溯法是一个既带有系统性又带有跳跃性的的搜索算法。它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。否则,进入该子树,继续按深度优先的策略进行搜索。回溯法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。而回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。这种以深度优先的方式系统地搜索问题的解的算法称为回溯法,它适用于解一些组合数较大的问题.

适用条件

问题的解用向量表示 X = (x1, x2, …, xn)
需要搜索一个或一组解 满足约束条件的最优解

2.三个概念

**约束函数:**约束函数是根据题意定出的。通过描述合法解的一般特征用于去除不合法的解,从而避免继续搜索出这个不合法解的剩余部分。因此,约束函数是对于任何状态空间树上的节点都有效、等价的。
**状态空间树:**一个问题的解可以表示成解向量X = (x1, x2, …, xn),X中每个分量xi所有取值的组合构成问题的解向量空间,简称解空间或者解空间树,又称为状态空间树,是一个对所有解的图形描述。树上的每个子节点的解都只有一个部分与父节点不同。
**扩展节点、活结点、死结点:**所谓扩展节点,就是当前正在求出它的子节点的节点,在DFS中,只允许有一个扩展节点。活结点就是通过与约束函数的对照,节点本身和其父节点均满足约束函数要求的节点;死结点反之。由此很容易知道死结点是不必求出其子节点的(没有意义)。
由于采用回溯法求解时存在退回到祖先结点的过程,所以需要保存搜索过的结点。通常采用: 定义栈来保存 采用递归方法
用回溯法通常采用两种策略(均称为剪枝函数)避免无效搜索。 用约束函数在扩展结点处剪除不满足约束条件的路径
用限界函数剪去得不到问题的解或最优解的路径

3.解题步骤

(1)描述解的形式,定义一个解空间,它包含问题的所有解,这一步主要明确问题的解空间树。
(2)确定结点的扩展搜索规则。
(3)构造约束函数(用于杀死节点)。然后就要通过DFS思想完成回溯,DFS可以采用递归回溯或者非递归(迭代)回溯。 确定问题的解空间 -->
确定结点的扩展搜索规则–> 以DFS方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。

一:符号三角形问题

1.问题描述

符号三角问题:下面都是“-”。 下图是由14个“+”和14个“-”组成的符号三角形。2个同号下面都是“+”,2个异号下面都是“-”。
在这里插入图片描述
在一般情况下,符号三角形的第一行有n个符号。符号三角形问题要求对于给定的n,计算有多少个不同的符号三角形,使其所含的“+”和“-”的个数相同。
参考代码如下,请在此基础上,实现如下功能:

  1. 分别输出n的值为1----20时,对应的符号三角形个数,如没有满足条件的符号三角形,则输出0
  2. 输入一个整数n,输出对应的符号三角形的个数,并依次显示出所有的符号三角形。

2.解题思路

(1)解题思路:
1、不断改变第一行的每个符号,搜索符合条件的解,可以使用递归回溯。
为了便于运算,设+ 为0,- 为1,这样可以使用异或运算符表示符号三角形的关系:
++为+即0^0=0, --为+即1^1=0, ±为-即0^1=1, -+为-即1^0=1
2、因为两种符号个数相同,可以对题解树剪枝
当所有符号总数为奇数时无解,当某种符号超过总数一半时无解
(2)算法设计
在符号三角形的第一行的前i个符号x[1:i]确定后,就确定了一个由i*(i+1)/2个符号组成的符号三角形。
下一步确定了x[1:i]的值后,只要在前面确定的符号三角形的右边加一条边,就可以扩展为x[1:i]所对应的符号三角形。
在回溯法搜索过程中可用当前符号三角形所包含的“+”号个数与“-”号个数均不超过n*(n-1)/4作为可行性约束,用于剪去不满足约束条件的子树。

3.程序代码

public  class Triangles{
   
 static int n, //第一行符号的个数
 count, //n*(n+1)/2
 half; //当前"+"的个数
 static int [][] p; //符号三角形矩阵
 static long sum; // 已找到的符号三角形数
 
 public static long compute(int nn)
{
   
      n=nn;
      count=0;
      sum=0;
      half=n*(n+1)/2;
      if (half%2==1) return 0;
      half=half/2;
      p=new int[n+1][n+1];
      for(int i=0;i<=n;i++)	
	 for(int j=0; j<=n;j++)
	   p[i][j]=0;
      backtrack(1);
      return sum;
}

 public static void backtrack (int t)
{
      
   if((count>half)||(t*(t-1)/2-count>half))
   {
      
       return;  //剪去不满足约束的子树
   }     
   if(t>n)
    {
      
    	sum++; //找到一个满足要求的三角形
        /*
           可在此处加入代码,输出当前解对应的符号三角形 
*/
    } 
   else
	for(int i=0;i<2;i++)
	{
   //子树
	   p[1][t]=i;
	   count+=i;
	   for(int j=2;j<=t;j++) //该子树形成的三角形
	   {
   
	      p[j][t-j+1]=p[j-1][t-j+1]^p[j-1][t-j+2];
	      count+=p[j][t-j+1];
	   }		   
           backtrack(t+1);
           for(int j=2;j<=t;j++) //回溯恢复
	     count-=p[j][t-j+1];
	   count-=i;
   }
}  

 public static void main(String[] args) {
   
     int n=4; long result;
     result=compute(n);
     System.out.println(String.format("result=%d",result));
  }  
}

package suanfa;
public class suanfa1 {
   
 
	static int n;//第一行的符号个数
	static int half;//n*(n+1)/4
	static int count;//当前“+”或者“-”的个数
	static int[][] p;//符号三角形矩阵
	static 
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

虚神公子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值