1、无死角搜索
1、数独游戏
1、问题
2、思想
(1)在输入上,将输入的每一行字符串转换为一个字符数组存入二位数组的一行
(2)深度搜索一般模板:
int dfs(t)
{
if(t>n)
{
print();
return ;
}
for(int i=1;<=n;i++i)
{
if(judge(t)) dfs(t+1);
}
}
(3)在dfs内部用检查是否符合条件的函数check进行设置,然后进入下一层dfs
(4)注意!这一层不符合条件返回之后要还原数组,将这个位置设置为‘0’
(5)不符合条件进入下一层dfs
(6)判断到最后符合条件了,直接输出并且使用System.exit(0)而不是return 因为此题只有唯一解,不用返回上一层,直接结束方法即可
3、代码
package com.lanqiao.eight;
import java.util.Scanner;
public class shudu {
public static void main(String[] args) {
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);//退出程序,不能用return
}
if(table[x][y]=='0')//此位无数字
{
for(int k=1;k<10;k++)
{
if(check(table,x,y,k))
{
table[x][y]=(char)('0'+k);
dfs(table,x+(y+1)/9,(y+1)%9);
//处理下一个状态,小技巧:当y==8的时候才会进入下一行
//当y==8的时候从下一行的第一个开始 (y+1)%9==0
}
}
table[x][y]='0';
}else {
dfs(table,x+(y+1)/9,(y+1)%9);//处理下一个状态
}
}
private static boolean check(char[][] table, int x, int y, int k) {
boolean flag = true;
char kl = (char)(k+'0');
//检查同行、同列
for(int i=0;i<table.length;i++)
{
if(table[x][i]==kl) return false;
}
for(int i=0;i<table[0].length;i++)
{
if(table[i][y]==kl) return false;
}
//检查小九宫格
int startX = x/3*3;
int startY = y/3*3;
for(int i=startX;i<(x/3+1)*3;i++)
{
for(int j=startY;j<(y/3+1)*3;j++)
{
if(table[i][j]==kl) return false;
}
}
return flag;
}
private static void print(char[][] table) {
for(char[] a:table)
{
for(char c:a)
{
System.out.print(c+" ");
}
System.out.println();
}
}
}
2、部分和
1、题目
2、思想
(1)相当于构造非空子集,在DFS过程中对每个元素有两种情况,一种要一种不要,逐渐深入。
(2)当满足条件的时候,退出
(3)因为要输出使用的元素,所以使用一个静态数组进行记录,但是要注意,dfs退回时进行回溯恢复数组
3、代码
package com.lanqiao.eight;
import java.util.LinkedList;
import java.util.Scanner;
public class findK {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] arr = new int[n];
for(int i=0;i<n;i++)
{
arr[i] = sc.nextInt();
}
int k = sc.nextInt();
dfs(arr,0, k);
}
static LinkedList<Integer> ans = new LinkedList<>();
private static void dfs(int[] arr, int cur, int k) {
if(k==0)
{
System.out.println("Yes!");
for(Integer i:ans)
{
System.out.print(i+" ");
}
System.exit(0);
}
if(k<0||cur==arr.length) return;
dfs(arr,cur+1,k);//不要当前元素
ans.add(arr[cur]);
dfs(arr,cur+1,k-arr[cur]);//要当前元素
//回溯
ans.remove(ans.size()-1);//LinkedList下标从0开始
}
}
3、水洼数目
1、题目
2、思想
(1)对每个W进入dfs
(2)将进入dfs的位置变为‘.’ 走围绕它一圈的八个位置,如果是W进入dfs
(3)知道全图无W结束
3、代码(经典,要背)
package com.lanqiao.eight;
import java.util.Scanner;
public class shuiwa {
static int M,N;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
M = sc.nextInt();
N = sc.nextInt();
int ans = 0;
char[][] table = new char[M][];
//table[0] = sc.nextLine().toCharArray();
for(int i=0;i<M;i++)
{
table[i] = sc.next().toCharArray();
}
for(int i = 0;i<M;i++)
{
for(int j= 0;j<N;j++) {
if(table[i][j]=='W')
{
ans++;
dfs(table,i,j);
}
}
}
System.out.println(ans);
}
private static void dfs(char[][] table, int x, int y) {
table[x][y] = '.';
for(int i=-1;i<=1;i++)
{
for(int j=-1;j<=1;j++)
{
if(i==0&&j==0) continue;
if(x+i<table.length&&y+j<table[0].length&&x+i>=0&&y+j>=0)
{
if(table[x+i][y+j]=='W')
{
dfs(table,x+i,y+j);
}
}
}
}
}
}
4、亮点:
(1)在外层利用判断条件进入dfs
(2)在内层直接变.(将水抽干)
5、注意点
(1)在进入dfs后首先要对当前点变为’ . ‘
(2)在输入时要采用next(),nextLine() 会吞上面留下来的回车,导致首行为空
回溯和剪枝:
剪枝:
在递归之前使用if判断是否需要进入这个状态,成为剪枝
4、N皇后问题
1、题目
2、思想
(1)判断对角线技巧
在正对角线的元素,x-y相同。反对角线上的元素x+y相同
(2)借助一个一位数组实现,下标代表行数,数据代表在这一行的第几列
(3)对每行的每列进行循环,判断是否满足条件,满足的时候向下一行进行传输
(4)
3、代码:
package com.lanqiao.eight;
import java.util.Scanner;
public class nQueen {
static int count = 0;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
int[] ans = new int[N];
dfs(ans, 0);
System.out.println(count);
}
//额。。col代表行,弄反了
public static void dfs(int[] arr, int col) {
if (col==arr.length)
{
count++;
return;
}
//尝试在当前行的任何一列放一个皇后
// for(int i=0;i<arr.length;i++)
// {
for (int j = 0; j < arr.length; j++) {
if (check(arr, col,j)) {
arr[col]=j;
dfs(arr, col + 1);
}
// }
}
}
private static boolean check(int[] arr,int col, int j) {
for(int i=0;i<col;i++)
{
//分别是某一行与当前选中列想通、正对角线和反对角线
if(arr[i]==j||i+arr[i]==col+j||arr[i]-i==col-j)
return false;
}
return true;
}
}
5、素数环
2、思想
(1)暴力破解法:把所有情况列出来,使用排列组合形式,然后对其进行一个一个判断
(2)dfs
逐步深入,然后剪枝
判断条件:
1、i在之前没有出现过
2、i和前面一个数相加是一个素数
3、代码
package com.lanqiao.eight;
import java.util.Scanner;
public class sushuCircle {
static int[] ans;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
ans = new int[N];
ans[0]=1;
dfs(N,1);
for(int i:ans)
{
System.out.print(i+" ");
}
}
private static void dfs(int n,int cur) {
if(cur==n&&isP(ans[0]+ans[n-1]))
{
return;
}
for(int i=2;i<=n;i++)
{
if(check(i,cur))
{
ans[cur]=i;
dfs(n,cur+1);
//可以不用回溯,因为后面会直接覆盖
}
}
}
private static boolean isP(int k) {
for(int i=2;i<k;i++)
{
if(k%i==0) return false;
}
return true;
}
private static boolean check(int i,int cur) {
for(int a:ans)
{
if(a==i||!isP(i+ans[cur-1])) return false;
}
return true;
}
}
4、注意点
(1)在得出结果时,要注意验证第一个和最后一个是否成立,成立即可形成封闭环
6、困难的串
1、问题
2、思想
判断条件:
是否前后截取相同
’
3、代码
package com.lanqiao.eight;
import java.util.Scanner;
public class difString {
static int count = 0;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int l = sc.nextInt();
dfs(l,n,"");
}
private static void dfs(int l, int n, String str) {
//尝试在str后面添加一个字符
for(char i='A';i<'A'+l;i++)
{
if(isHard(str,i))//判断是否是困难的串
{
String x = str+i;
System.out.println(x);//输出
count++;//计数
if(count==n)//判断是否判断到了这一个
{
System.exit(0);//已经到达这一难度,退出
}
dfs(l,n,x);//进行下一步判断
}
}
}
private static boolean isHard(String str, char c) {
int count = 0;//截取的宽度
for(int i=str.length()-1;i>=0;i-=2)
{
final String s1 = str.substring(i,i+count+1);
final String s2 = str.substring(i+count+1)+c;
if(s1.equals(s2))
return false;
count++;
}
return true;
}
}