深入递归回溯剪枝

本文探讨如何利用递归回溯算法解决数独问题和n皇后问题,通过实例展示了递归回溯在解决这类约束满足问题上的有效性。同时,还涉及了寻找困难字符串的问题,展示递归回溯在不同场景的应用。
摘要由CSDN通过智能技术生成


/**
 * 输入正整数n,对1-n进行排列,使得相邻两个数之和均为素数, 输出时从整数1开始,逆时针排列。同一个环应恰好输出一次。 n<=16
 *
 * 如输入:6 输出: 1 4 3 2 5 6 1 6 5 2 3 4
 */

public class _dfs素数环 {
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();
		int[] r = new int[n];
		r[0] = 1;
		dfs(n, r, 1);
	}

	private static void dfs(int n, int[] r, int cur) {
		if (cur == n && isP(r[0] + r[n - 1])) {// 填到末尾了,还有首尾相加为素数才算成功
			print(r);
			return;

		}
		for (int i = 2; i <= n; i++) {// 尝试用每个数字填到cur这个位置
			if (check(r, i, cur)) {// r中没有i这个数,且和上一个数之和为素数
				r[cur] = i;// 试着将i放在cur位置,往前走一步
				dfs(n, r, cur + 1);
				r[cur] = 0;// 回溯
			}

		}
	}

	private static void print(int[] r) {
		for (int i = 0; i < r.length; i++) {
			System.out.print(r[i] + (i == r.length - 1 ? "" : " "));
		}
		System.out.println();
	}

	private static boolean check(int[] r, int i, int cur) {
		for (int e : r) {
			if (e == i || !isP(r[cur - 1] + i))
				return false;
		}
		return true;
	}

	private static boolean isP(int k) {
		for (int i = 2; i * i <= k; i++) {
			if (k % i == 0)
				return false;
		}
		return true;

	}
}

2.

/*
你一定听说过“数独”游戏。
如下图所示,玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个同色九宫内的数字均含1-9,不重复。
数独的答案都是唯一的,所以,多个解也称为无解。
本图的数字据说是芬兰数学家花了3个月的时间设计出来的较难的题目。但对会使用计算机编程的你来说,恐怕易如反掌了。
本题的要求就是输入数独题目,程序输出数独的唯一解。我们保证所有已知数据的格式都是合法的,并且题目有唯一的解。
格式要求,输入9行,每行9个数字,0代表未知,其它数字为已知。
输出9行,每行9个数字表示数独的解。
输入:

005300000
800000020
070010500
400005300
010070006
003200080
060500009
004000030
000009700

程序应该输出:

145327698
839654127
672918543
496185372
218473956
753296481
367542819
984761235
521839764

再例如,输入:

800000000
003600000
070090200
050007000
000045700
000100030
001000068
008500010
090000400

程序应该输出:

812753649
943682175
675491283
154237896
369845721
287169534
521974368
438526917
796318452
*/

public class _Dfs数独 {
  public static void main(String[] args) {
    // System.out.println((char)('0'+1));
    Scanner sc = new Scanner(System.in);
    char[][] table = new char[9][];
    for (int i = 0; i < 9; i++) {
      table[i] = sc.nextLine().toCharArray();
    }
    dfs(table, 0, 0);
  }

  private static void dfs(char[][] table, int x, int y) {
    if (x == 9) {
      print(table);
      System.exit(0);
    }
    if (table[x][y] == '0') {//虚位以待
      for (int k = 1; k < 10; k++) {
        if (check(table, x, y, k)) {
          // f = false;
          table[x][y] = (char) ('0' + k);
          dfs(table, x + (y + 1) / 9, (y + 1) % 9);//处理下一个状态
        }
      }
      table[x][y] = '0';//回溯

    } else {
      dfs(table, x + (y + 1) / 9, (y + 1) % 9);//处理下一个状态
    }

  }

  private static void print(char[][] table) {
    for (int i = 0; i < 9; i++) {
      System.out.println(new String(table[i]));
    }
  }

  private static boolean check(char[][] table, int i, int j, int k) {
    //检查同行和同列
    for (int l = 0; l < 9; l++) {
      if (table[i][l] == (char) ('0' + k)) return false;
      if (table[l][j] == (char) ('0' + k)) return false;
    }
    //检查小九宫格
    for (int l = (i / 3) * 3; l < (i / 3 + 1) * 3; l++) {
      for (int m = (j / 3) * 3; m < (j / 3 + 1) * 3; m++) {
        if (table[l][m] == (char) ('0' + k)) return false;
      }
    }
    return true;
  }

}

 

/**
 * 问题描述:如果一个字符串包含两个相邻的重复子串,则称它为容易的串,其他串称为困难的串,
 * 如:BB,ABCDACABCAB,ABCDABCD都是容易的,A,AB,ABA,D,DC,ABDAB,CBABCBA都是困难的。

 输入正整数n,L,输出由前L个字符(大写英文字母)组成的,字典序第n小的困难的串。
 例如,当L=3时,前7个困难的串分别为:
 A,AB,ABA,ABAC,ABACA,ABACAB,ABACABA
 n指定为4的话,输出ABAC
 */

public class _dfs困难的串 {
   public static void main(String[] args) {
	  int n=10;
	  int l=4;
	 dfs(l,n,"");
	 // isHard("0123020120",1);
}
   static int count;
   
   private static void dfs(int l,int n,String prefix) {
	  
	   //尝试在prefix后追加一个字符
	   for(char i='A';i<'A'+l;i++) {
		   if(isHard(prefix,i)) {//是困难的串,就组合起来输出
			   String x=prefix+i;
			   System.out.println(x);
			   count++;//计数
			   if(count==n) 
				   System.exit(0);
			   
			   dfs(l,n,x);
			   
		   }
	   }   
   
}
   /**
    * 判断prefix+i是否一个困难的串
    * 1.遍历所有的长度为偶数的子串,看是否对称
    * 2.prefix是一个困难的串 ABACA i
    * @param prefix
    * @param i
    * @return
    */
   private static boolean isHard(String prefix, char i) {
	    int count = 0;//截取的宽度
	    for (int j = prefix.length() - 1; j >= 0; j -= 2) {
	      final String s1 = prefix.substring(j, j + count + 1);
	      final String s2 = prefix.substring(j + count + 1) + i;
	      if (s1.equals(s2))
	        return false;
	      count++;
	    }
	    return true;
	  }
}

/*
给定整数序列a1,a2,...,an,判断是否可以从中选出若干数,使它们的和恰好为k.

    1≤n≤20

    -10^8≤ai≤10^8

    -10^8≤k≤10^8

样例:

输入

    n=4
    a={1,2,4,7}
    k=13
输出:

    Yes (13 = 2 + 4 + 7)

*/

public class _Dfs部分和 {

  private static int kk;

  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    int n = sc.nextInt();
    int[] a = new int[n];
    for (int i = 0; i < n; i++) {
      a[i] = sc.nextInt();
    }
    int k = sc.nextInt();//13
    kk = k;
    dfs(a, k, 0, new ArrayList<Integer>());
  }


  private static void dfs(int[] a, int k, int cur, ArrayList<Integer> ints) {
    if (k == 0) {
      System.out.print("Yes (" + kk + " = ");
      int size = ints.size();
      for (int i = 0; i < size; i++) {
        System.out.print(ints.get(i) + (i == size - 1 ? "" : " + "));
      }
      System.out.println(")");
      exit(0);
    }
    if (k < 0 || cur == a.length) return;

    dfs(a, k, cur + 1, ints);//不要cur这个元素

    ints.add(a[cur]);
    int index = ints.size() - 1;
    dfs(a, k - a[cur], cur + 1, ints);
    ints.remove(index);//回溯
  }
}

/*
 * /**
 * 请设计一种算法,解决著名的n皇后问题。这里的n皇后问题指在一个n*n的棋盘上放置n个棋子,
 * 使得每行每列和每条对角线上都只有一个棋子,求其摆放的方法数。

 给定一个int n,请返回方法数,保证n小于等于15
 */

public class _dfs4n皇后问题 {
  static int n;
  static int cnt;
  static int[] rec;
  
  public static void main(String[] args) {
	n=4;
	rec=new int[4];
	dfs(0);
	System.out.println(cnt);
}
  /*
   * row  当前正在处理的行
   */
  private static void dfs(int row) {
	  if(row==n) {
		  cnt++;
		  return;
	  }
	  //一次尝试在某个列上放一个皇后
	  for(int col=0;col<n;col++) {
		  boolean ok=true;
		  //检验这个皇后是否和之前已经放置的皇后有冲突
		  for(int i=0;i<row;i++) {
			  if(rec[i]==col||i+rec[i]==row+col||rec[i]-i==col-row) {
				  ok=false;
				  break;
			  }
		  }
		  //这里可以认为剪枝
		  //这一行的这一列可以放
		  if(ok) {
			  rec[row]=col;//标记
			  dfs(row+1);//继续找下一行
			  //rec[row]=0;//恢复原状,这种解法这里是否恢复状态都行,为什么?
		  }
	  }
  }
  
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值