了解了回溯算法的原理后,下面举两个例子来看回溯算法的简单应用
一、问题一:N皇后问题
1. 问题引入:
八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 下面我们来解N皇后问题。
2. 思路分析:
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. 问题引入:

填入0~9的数字。要求:连续的两个数字不能相邻。
(左右、上下、对角都算相邻)
一共有多少种可能的填数方案?
2. 思路分析
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>