算法(二)回溯算法简单应用

了解了回溯算法的原理后,下面举两个例子来看回溯算法的简单应用

一、问题一:N皇后问题 

1. 问题引入:

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 下面我们来解N皇后问题。

2. 思路分析:

 
八皇后问题运用回溯法,从第一行开始,每行放入一个,首先每一行放置均会循环,也就是每一行的皇后都会被依次放置在8个位置上,如果满足条件再摆下一行,直到摆完最后一行,视为一种解法;反之,如果条件不满足,首先清掉所有这一行摆放的记录,继续试探下一个位置。这里的试探就比较简单了,可以根据题目条件写一个返回值为boolean的函数。现在我们根据这样的思路来解决N皇后的问题。
 

3. 代码:

<span style="color:#330099">package 回溯;

import java.util.Scanner;

public class N皇后问题 {
	static int  count=0;
	public static void main(String args[]){
		int n=new Scanner(System.in).nextInt();
		int a[][]=new int[n][n];
		for(int i=0;i<n;i++){
			for(int j=0;j<n;j++){
				//初始化
				a[i][j]=0;
			}
		}
		fun(a,0,n); //从第1行开始排
		System.out.print(count);
	}
	public static void fun(int[][] a,int row,int n){
		if(row==n){
			count++;
			
		}else{
			for(int i=0;i<n;i++){
				for(int j=0;j<n;j++) a[row][j]=0;  //改行记录清零
				a[row][i]=1;
				if(check(a,row,i,n)) fun(a,row+1,n);
			}
			
		}
	}
	
	public static boolean check(int[][] a,int row,int col,int n){
		
		int step=1;
        while(row-step>=0){
            if(a[row-step][col]==1)                //中上
                return false;
            if(col-step>=0 && a[row-step][col-step]==1)        //左上
                return false;
            if(col+step<n && a[row-step][col+step]==1)        //右上
                return false;
            
            step++;
        }
        return true;
    }
}</span>
 

二、问题二:方格填数问题 

1. 问题引入:

如下的10个格子



填入0~9的数字。要求:连续的两个数字不能相邻。
(左右、上下、对角都算相邻)
一共有多少种可能的填数方案?

2. 思路分析

思路分析:利用回溯法,一个空格一个空格的填充,每次填充可以是0~9之间的任一个数,然后判断条件,如果符合,继续填充下一个空格,否则继续试探下一个数字。这里判断的条件也很简单,因为是一个一个的累加填充,所以只需考虑其左边,上面,左上角,右上角是否相邻,当然前提是填入的数字不相等。代码如下:

3. 代码如下:

<span style="color:#330099">package 回溯;  
  
public class 方格填数 {  
    private static int count=0;  
    public static void main(String args[]){  
        int a[]=new int[10];  
        for(int i=0;i<10;i++){  
            a[i]=20; //初始化为一个不相干的数即可  
        }  
        fun(a,0);  //从第1个空格开始,即a[0]  
        System.out.print(count);  
    }  
    public static void fun(int a[],int n){  
        //填充第n+1个,即a[n]的值  
        if(n==10){  
              
            count++;  
        }else{  
            for(int i=0;i<=9;i++){  
                a[n]=i;  
                if(check(a,n)) fun(a,n+1);  
            }  
        }  
    }  
      
    public static  boolean check(int a[],int n){  
        //判断,因为是逐个往后摆的,所以需考虑中上,左上,右上和左边  
        boolean flag=true;  
        if(n!=0){  
          for(int i=0;i<n;i++){  //数字不重复  
            if(a[i]==a[n]){  
                flag=false;  
                break;  
            }  
            }  
        }  
        if((n!=0 && n!=3 && n!=7) && Math.abs(a[n]-a[n-1])==1)  
            flag=false; //左边  
        if(n>3 && Math.abs(a[n]-a[n-4])==1)  
            flag=false;  //中上  
        if(n>4 && n!=7 && Math.abs(a[n]-a[n-5])==1)  
            flag=false;  //左上  
        if(n>2 && n!=6 && Math.abs(a[n]-a[n-3])==1)  
            flag=false; //右上  
          
        return flag;  
    }  
      
  
}  </span>
 

二、李白打酒问题 

1.问题引入:

    话说大诗人李白,一生好饮。幸好他从不开车。
    一天,他提着酒壶,从家里出来,酒壶中有酒2斗。他边走边唱:
    无事街上走,提壶去打酒。
    逢店加一倍,遇花喝一斗。
    这一路上,他一共遇到店5次,遇到花10次,已知最后一次遇到的是花,他正好把酒喝光了。 
    话说大诗人李白,一生好饮。幸好他从不开车。
    一天,他提着酒壶,从家里出来,酒壶中有酒2斗。他边走边唱:
    无事街上走,提壶去
打酒。
    逢店加一倍,遇花喝一斗。
    这一路上,他一共遇到店5次,遇到花10次,已知最后一次遇到的是花,他正好把酒喝光了。 
    请你计算李白遇到店和花的次序,可以把遇店记为a,遇花记为b。则:babaabbabbabbbb 就是合理的次序。像这样的答案一共有多少呢?

2.思路分析:

已知最后一种情况,则只要填充前面14次即可。回溯的条件判断越严谨,效率越高,所以不要把所有的条件放在最后一次填充时判断。而是每次填充时,判断一下酒量和店,花的次数是否符合。

3.代码如下:

 
<span style="color:#330099">package 回溯;

public class 李白打酒 {
	private static int count=0;
	public static void main(String args[]){
		int a[]=new int[14];
		for(int i=0;i<14;i++){
			a[i]=0;
		}
		fun(a,0);
		System.out.print(count);
	}
	public static void fun(int a[],int n){
		if(n==14 && check(a,13)){
			
			count++;
			for(int i=0;i<14;i++){
				System.out.print(a[i]);
			}
			System.out.println();
			return ;
		}else{
			
				for(int j=0;j<2;j++){
					a[n]=j;
					if(check(a,n)) fun(a,n+1);
				
			}
		}
	}
	public static boolean check(int a[],int n){
		boolean flag=true;
		int c=2;//初始
		int x=0;  //遇到店,即啊a[n]=0;
		int y=0;//遇到花,即a[n]=1;
		if(n!=13){
			for(int i=0;i<=n;i++){
			if(a[i]==0){
				c=c*2;
				x++;
			}else if(a[i]==1){
				c=c-1;
				y++;
			}
		}
		if(c<0 || x>5 || y>9) flag=false;
		}
		
		
		
		
		if(n==13){//用于最后的判断
			for(int i=0;i<=n;i++){
				if(a[i]==0){
					c=c*2;
					x++;
				}else if(a[i]==1){
					c=c-1;
					y++;
				}
			}
			if(c!=1 || x!=5 || y!=9) flag=false;
		}
		return flag;
	

	}
}</span>


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

w_t_y_y

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

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

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

打赏作者

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

抵扣说明:

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

余额充值