不撞南墙不回头——深度优先搜索
问题描述
输入一个自然数N(1<=N<=9),从小到大输出用1~N组成的所有排列,也就说全排列。例如输入3则输出
123
132
213
231
312
321
输入格式:
输入一个自然数N(1<=N<=9)
输出格式:
N的全排列,每行一个
限制:
每个测试点1秒
样例 1 :
输入:
2
输出:
12
21
样例 2 :
输入:
3
输出:
123
132
213
231
312
321
package com.qianwei.chapter4;
import java.util.Scanner;
public class DFSTest1 {
static int[] a = new int[10];
static int[] book = new int[10];
static int n;
public static void dfs(int step) {
if (step == n+1) {
for (int i=1; i<=n; i++)
System.out.print(a[i]);
System.out.println();
return;
}
for (int i=1; i<=n; i++) {
if (book[i] == 0) {
a[step] = i;
book[i] = 1;
dfs(step+1);
book[i] = 0;
}
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
dfs(1);
sc.close();
}
}
用本节新学的DFS算法重新解决第3章的第一节 ???+???=???这个问题。
package com.qianwei.chapter4;
public class DFSTest2 {
static int[] a = new int[9];
static int[] book = new int[9];
static int count = 0;
public static void dfs(int step) {
if (step == 9) {
if (a[0]*100+a[1]*10+a[2]+a[3]*100+a[4]*10+a[5] == a[6]*100+a[7]*10+a[8]) {
count++;
System.out.println(""+a[0]+a[1]+a[2]+" "+a[3]+a[4]+a[5]+" "+a[6]+a[7]+a[8]);
}
return;
}
for (int i=0; i<9; i++) {
if (book[i] == 0) {
a[step] = i+1;
book[i] = 1;
dfs(step+1);
book[i] = 0;
}
}
return;
}
public static void main(String[] args) {
dfs(0);
System.out.println(count/2);
}
}
解救小哈
问题描述
有一天,小哈一个去玩迷宫。但是方向感很不好的小哈很快就迷路了。小哼得知后便立即去解救无助的小哈。小哼当然是有备而来,已经弄清楚了迷宫地图,现在小哼要以最快速度去解救小哈。问题就此开始了……
迷宫由n行m列的单元格组成,每个单元格要么是空地,要么是障碍物。你的任务是帮助小哼找到一条从迷宫的起点到小哈所在位置的最短路径,注意障碍物是不能走的,当然也不能走到迷宫之外。n和m都小于等于100。
输入格式:
第一行有两个数N M。N表示迷宫的行,M表示迷宫的列。接来下来N行M列为迷宫,0表示空地,1表示障碍物。最后一行4个数,前两个数为迷宫入口的x和y坐标。后两个为小哈的x和y坐标。
输出格式:
一个整数表示小哼到小哈的最短步数。如果不能解救小哈则输出No Way!
提示:
RQNOJ 34紧急援救
195校园迷宫
样例 1 :
输入:
5 4
0 0 1 0
0 0 0 0
0 0 1 0
0 1 0 0
0 0 0 1
1 1 4 3
输出:
7
样例 2 :
输入:
3 3
1 1 1
0 1 0
0 1 0
2 1 3 3
输出:
No Way!
package com.qianwei.chapter4;
import java.util.Scanner;
public class RescueXiaoHaDFS {
static int n; //n行
static int m; //m列
static int p; //小哈所在位置
static int q;
static int min = 1000;
static int[][] a = new int[51][51]; //根据题目要求单元格的行和列都不超过50
static int[][] book = new int[51][51];
static void dfs(int x, int y, int step) {
int[][] next = {{0,1},{1,0},{0,-1},{-1,0}}; //右下左上
if (x==p && y==q) {
if (step < min)
min = step;
return;
}
for (int k=0;k<4;k++) {
//下一个试探的点的坐标
int tx = x+next[k][0];
int ty = y+next[k][1];
if (tx<1 || tx>n || ty<1 || ty>m) //判断是否越界
continue;
if (a[tx][ty]==0 && book[tx][ty]==0) {
book[tx][ty] = 1;
dfs(tx,ty,step+1);
book[tx][ty] = 0;
}
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
a[i][j] = sc.nextInt();
int startx = sc.nextInt();
int starty = sc.nextInt();
p = sc.nextInt();
q = sc.nextInt();
book[startx][starty] = 1;
dfs(startx,starty,0);
System.out.println(min);
sc.close();
}
}
层层递进——广度优先搜索
问题描述
将上一节的题目用广度优先搜索的方式实现
package com.qianwei.chapter4;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Scanner;
class Node {
int x;
int y;
int s;
Node(int x, int y, int s){
this.x = x;
this.y = y;
this.s = s;
}
}
public class RescueXiaoHaBFS {
static int[][] a = new int[51][51];
static int[][] book = new int[51][51];
static Deque<Node> queue = new LinkedList<Node>();
static int n, m, p, q;
static int flag = 0;
public static void bfs() {
int[][] next = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
while (!queue.isEmpty()) {
for (int i = 0; i < 4; i++) {
int tx = queue.peek().x + next[i][0];
int ty = queue.peek().y + next[i][1];
if (tx < 1 || tx > n || ty < 1 || ty > m)
continue;
if (a[tx][ty] == 0 && book[tx][ty] == 0) {
//标记为已拓展,不同于DFS的是,BFS每个点只会被拓展一次,无需进行回溯
book[tx][ty] = 1;
queue.add(new Node(tx, ty, queue.peek().s + 1));
}
if (tx == p && ty == q) {
flag = 1;
break;
}
}
if (flag == 1)
break;
queue.poll();
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
a[i][j] = sc.nextInt();
}
}
int startX = sc.nextInt();
int startY = sc.nextInt();
p = sc.nextInt();
q = sc.nextInt();
queue.add(new Node(startX, startY, 0));
book[startX][startY] = 1;
bfs();
if (flag == 1)
System.out.println(queue.getLast().s);
else
System.out.println("No Way!");
sc.close();
}
}
第4节 再解炸弹人
问题描述
现在炸弹不是想放在那里就能放在那里的了,必须由小人能够走到的地方才能放置炸弹。比如下面这个例子小人默认站在(3,3)这个位置。请问放在何处最多可以消灭多个敌人。
输入格式:
第一行4个整数为n m x y,分别n和m表示迷宫的行和列,x和y表示小人的起始坐标(从0行0列开始计算),接下来的n行m列为地图。
1<=n,m<=50
输出格式:
最多可以消灭的敌人数。
样例 1 :
输入:
13 13 3 3
#############
#GG.GGG#GGG.#
###.#G#G#G#G#
#.......#..G#
#G#.###.#G#G#
#GG.GGG.#.GG#
#G#.#G#.#.#.#
##G...G.....#
#G#.#G###.#G#
#...G#GGG.GG#
#G#.#G#G#.#G#
#GG.GGG#G.GG#
#############
输出:
10
说明:
将炸弹放置在(7,11)处,最多可以消灭10个敌人。
package com.qianwei.chapter4;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Scanner;
class NodeBomberman {
int x;
int y;
NodeBomberman(int x, int y) {
this.x = x;
this.y = y;
}
}
public class BombermanBFS {
static char[][] a = new char[20][20];
static int[][] book = new int[20][20];
static Deque<NodeBomberman> queue = new LinkedList<NodeBomberman>();
static int n, m;
static int max=0, mx, my;
public static void bfs() {
int sum;
int[][] next = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
while (!queue.isEmpty()) {
for (int i = 0; i < 4; i++) {
int tx = queue.peek().x + next[i][0];
int ty = queue.peek().y + next[i][1];
if (tx < 0 || tx > n - 1 || ty < 0 || ty > m - 1)
continue;
if (a[tx][ty] == '.' && book[tx][ty] == 0) {
book[tx][ty] = 1;
queue.add(new NodeBomberman(tx, ty));
sum = getnum(tx, ty);
if (sum > max) {
max = sum;
mx = tx;
my = ty;
}
}
}
queue.poll();
}
}
//统计该点可以消灭的敌人数
public static int getnum(int i, int j) {
int sum=0;
int x = i;
int y = j;
while (a[x][y] != '#') {
if (a[x][y] == 'G') {
sum++;
}
x--;
}
x = i;
y = j;
while (a[x][y] != '#') {
if (a[x][y] == 'G') {
sum++;
}
x++;
}
x = i;
y = j;
while (a[x][y] != '#') {
if (a[x][y] == 'G') {
sum++;
}
y--;
}
x = i;
y = j;
while (a[x][y] != '#') {
if (a[x][y] == 'G') {
sum++;
}
y++;
}
return sum;
}
public static void main(String[] args) {
//"#"代表墙,"G"代表怪物,"."代表放置炸弹的位置
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
int startX = sc.nextInt();
int startY = sc.nextInt();
for (int i = 0; i < n; i++) {
String str = sc.next();
a[i] = str.toCharArray();
}
queue.add(new NodeBomberman(startX, startY));
max = getnum(startX, startY);
mx = startX;
my = startY;
bfs();
System.out.println(mx + " " + my + " " + max);
sc.close();
}
}
用深度优先搜索实现
package com.qianwei.chapter4;
import java.util.Scanner;
public class BombermanDFS {
static char[][] a = new char[20][20];
static int[][] book = new int[20][20];
static int max;
static int mx, my;
static int n, m;
public static void dfs(int x, int y) {
int sum;
int[][] next = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
sum = getsum(x, y);
if (sum > max) {
max = sum;
mx = x;
my = y;
}
for (int i = 0; i < 4; i++) {
int tx = x + next[i][0];
int ty = y + next[i][1];
if (tx < 0 || tx > n - 1 || ty < 0 || ty > m - 1)
continue;
if (a[tx][ty] == '.' && book[tx][ty] == 0) {
book[tx][ty] = 1;
dfs(tx, ty);
}
}
return;
}
public static int getsum(int i, int j) {
int x, y;
int sum = 0;
x = i;
y = j;
while (a[x][y] != '#') {
if (a[x][y] == 'G') {
sum++;
}
x--;
}
x = i;
y = j;
while (a[x][y] != '#') {
if (a[x][y] == 'G') {
sum++;
}
x++;
}
x = i;
y = j;
while (a[x][y] != '#') {
if (a[x][y] == 'G') {
sum++;
}
y--;
}
x = i;
y = j;
while (a[x][y] != '#') {
if (a[x][y] == 'G') {
sum++;
}
y++;
}
return sum;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
int startX = sc.nextInt();
int startY = sc.nextInt();
for (int i = 0; i < n; i++) {
String str = sc.next();
a[i] = str.toCharArray();
}
book[startX][startY] = 1;
max = getsum(startX, startY);
mx = startX;
my = startY;
dfs(startX, startY);
System.out.println(mx + " " + my + " " + max);
sc.close();
}
}
第5节 宝岛探险
问题描述
小哼通过秘密方法得到一张不完整的钓鱼岛航拍地图。钓鱼岛由一个主岛和一些附属岛屿组成,小哼决定去钓鱼岛探险。下面这个10*10的二维矩阵就是钓鱼岛的航拍地图。图中数字表示海拔,0表示海洋,1~9都表示陆地。小哼的飞机将会降落在(6,8)处,现在需要计算出小哼降落所在岛的面积(即有多少个格子)。注意此处我们把与小哼降落点上下左右相链接的陆地均视为同一岛屿。
1 2 1 0 0 0 0 0 2 3
3 0 2 0 1 2 1 0 1 2
4 0 1 0 1 2 3 2 0 1
3 2 0 0 0 1 2 4 0 0
0 0 0 0 0 0 1 5 3 0
0 1 2 1 0 1 5 4 3 0
0 1 2 3 1 3 6 2 1 0
0 0 3 4 8 9 7 5 0 0
0 0 0 3 7 8 6 0 1 2
0 0 0 0 0 0 0 0 1 0
输入格式:
一行4个整数,前两个整数表示n行m列,后两个整数表示降落的坐标x行y列
输出格式:
一个整数表示岛屿的面积
限制:
n<=100
m<=100
样例 1 :
输入:
10 10 6 8
1 2 1 0 0 0 0 0 2 3
3 0 2 0 1 2 1 0 1 2
4 0 1 0 1 2 3 2 0 1
3 2 0 0 0 1 2 4 0 0
0 0 0 0 0 0 1 5 3 0
0 1 2 1 0 1 5 4 3 0
0 1 2 3 1 3 6 2 1 0
0 0 3 4 8 9 7 5 0 0
0 0 0 3 7 8 6 0 1 2
0 0 0 0 0 0 0 0 1 0
输出:
38
package com.qianwei.chapter4;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Scanner;
class NodeIsland {
int x;
int y;
NodeIsland(int x, int y) {
this.x = x;
this.y = y;
}
}
public class IslandBFS {
static int[][] a = new int[50][50];
static int[][] book = new int[50][50];
static int n, m;
static int sum = 1;
static Deque<NodeIsland> queue = new LinkedList<NodeIsland>();
public static void bfs() {
int[][] next = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
while (!queue.isEmpty()) {
for (int i = 0; i < 4; i++) {
int tx = queue.peek().x + next[i][0];
int ty = queue.peek().y + next[i][1];
if (tx < 0 || tx > n - 1 || ty < 0 || ty > n - 1)
continue;
if(a[tx][ty] > 0 && book[tx][ty] == 0) {
queue.add(new NodeIsland(tx, ty));
sum++;
book[tx][ty] = 1;
}
}
queue.poll();
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
int startX = sc.nextInt();
int startY = sc.nextInt();
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
a[i][j] = sc.nextInt();
}
}
queue.add(new NodeIsland(startX, startY));
book[startX][startY] = 1;
bfs();
System.out.println(sum);
sc.close();
}
}
用深度优先搜索实现
package com.qianwei.chapter4;
import java.util.Scanner;
public class IslandDFS {
static int[][] a = new int[50][50];
static int[][] book = new int[50][50];
static int sum = 1;
static int n, m;
public static void dfs(int x, int y) {
int[][] next = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
for (int i = 0; i < 4; i++) {
int tx = x + next[i][0];
int ty = y + next[i][1];
if(tx < 0 || tx > n - 1 || ty < 0 || ty > n - 1)
continue;
if (a[tx][ty] > 0 && book[tx][ty] == 0) {
sum ++;
book[tx][ty] = 1;
dfs(tx, ty);
}
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
int startX = sc.nextInt();
int startY = sc.nextInt();
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
a[i][j] = sc.nextInt();
}
}
book[startX][startY] = 1;
dfs(startX, startY);
System.out.println(sum);
sc.close();
}
}
将上面的代码稍作改动,将下哼降落的岛屿都改为-1,表示该岛屿已经被小哼玩遍了。
package com.qianwei.chapter4;
import java.util.Scanner;
public class IslandDFScolor {
static int[][] a = new int[50][50];
static int[][] book = new int[50][50];
static int sum = 1;
static int n, m;
public static void dfs(int x, int y, int color) {
int[][] next = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
a[x][y] = color;
for (int i = 0; i < 4; i++) {
int tx = x + next[i][0];
int ty = y + next[i][1];
if(tx < 0 || tx > n - 1 || ty < 0 || ty > n - 1)
continue;
if (a[tx][ty] > 0 && book[tx][ty] == 0) {
sum ++;
book[tx][ty] = 1;
dfs(tx, ty, color);
}
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
int startX = sc.nextInt();
int startY = sc.nextInt();
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
a[i][j] = sc.nextInt();
}
}
book[startX][startY] = 1;
dfs(startX, startY, -1);
System.out.println(sum);
sc.close();
}
}
如果想知道这个地图中有多少个独立的小岛又改怎么做?下面采用DFS的方法实现。
package com.qianwei.chapter4;
import java.util.Scanner;
public class IslandFloodfillDFS {
static int[][] a = new int[50][50];
static int[][] book = new int[50][50];
static int sum = 1;
static int ant = 0;
static int n, m;
public static void dfs(int x, int y, int color) {
int[][] next = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
a[x][y] = color;
for (int i = 0; i < 4; i++) {
int tx = x + next[i][0];
int ty = y + next[i][1];
if(tx < 0 || tx > n - 1 || ty < 0 || ty > n - 1)
continue;
if (a[tx][ty] > 0 && book[tx][ty] == 0) {
sum ++;
book[tx][ty] = 1;
dfs(tx, ty, color);
}
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
a[i][j] = sc.nextInt();
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (a[i][j] > 0) {
ant--;
book[i][j] = 1;
dfs(i, j, ant);
}
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (a[i][j] < 0)
System.out.print(a[i][j] + "\t");
else
System.out.print(" "+a[i][j] + "\t");
}
System.out.println();
}
System.out.println("有"+(-ant)+"个小岛");
sc.close();
}
}
采用BFS方法实现。
package com.qianwei.chapter4;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Scanner;
public class IslandFloodfillBFS {
static int[][] a = new int[50][50];
static int[][] book = new int[50][50];
static int ant = 0;
static int n, m;
static Deque<NodeIsland> queue = new LinkedList<NodeIsland>();
public static void bfs(int x, int y, int color) {
int[][] next = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
a[x][y] = color;
while (!queue.isEmpty()) {
for (int i = 0; i < 4; i++) {
int tx = queue.peek().x + next[i][0];
int ty = queue.peek().y + next[i][1];
if (tx < 0 || tx > n - 1 || ty < 0 || ty > n - 1)
continue;
if(a[tx][ty] > 0 && book[tx][ty] == 0) {
a[tx][ty] = color;
queue.add(new NodeIsland(tx, ty));
book[tx][ty] = 1;
}
}
queue.poll();
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
a[i][j] = sc.nextInt();
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (a[i][j] > 0) {
ant--;
queue.add(new NodeIsland(i, j));
book[i][j] = 1;
bfs(i, j, ant);
}
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (a[i][j] < 0)
System.out.print(a[i][j] + "\t");
else
System.out.print(" "+a[i][j] + "\t");
}
System.out.println();
}
System.out.println("有"+(-ant)+"个小岛");
sc.close();
}
}
第6节 水管工游戏
问题描述
这小节有点难,看不太懂可以跳过哦。
最近小哼又迷上一个叫做水管工的游戏。游戏的大致规则是这样的。一块矩形土地被分为N * M的单位正方形,现在这块土地上已经埋设有一些水管,水管将从坐标为(1,1)左上角左部边缘,延伸到(N,M)右下角右部边缘。水管只有2种,如下图所示。
每种管道将占据一个单位正方形土地。你现在可以旋转这些管道,使得构成一个管道系统,即创造一条从(1,1)到(N,M)的连通管道。标有树木的方格表示这里没有管道。如下图:一个4*5的土地中(4,2)处有一个树木。
我们可以旋转其中的一些管道,使之构成一个连通的管道系统,如下图。
如果通过旋转管道可以使之构成一个连通的管道系统,就输出铺设的路径,否则粗出impossible。
输入格式:
输入的第一行为两个整数N和 M(都不超过10),接下来的N行,每行有M个整数,表示地图中的每一小格。其中0表示树木,1~6分别表示管道的六种不同的摆放方式
输出格式:
样例 1 :
输入:
5 4
5 3 5 3
1 5 3 0
2 3 5 1
6 1 1 5
1 5 5 4
输出:
(1,1) (1,2) (2,2) (3,2) (3,3) (3,4) (4,4) (5,4)
package com.qianwei.chapter4;
import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Scanner;
class NodePlumber {
int x;
int y;
NodePlumber(int x, int y){
this.x = x;
this.y = y;
}
}
public class PlumberGame {
static int[][] a = new int[51][51];
static int[][] book = new int[51][51];
static int n;
static int m;
static int flag = 0;
static Deque<NodePlumber> stack = new LinkedList<NodePlumber>();
static void dfs(int x, int y, int front) {
if (x==n && y==m+1) {
flag = 1;
Iterator<NodePlumber> it = stack.descendingIterator();
while (it.hasNext()) {
//一开始用下面这条代码,输出结果报错了,真是愚蠢
// System.out.print("("+it.next().x+","+it.next().y+")"+" ");
NodePlumber temp = it.next();
System.out.print("("+temp.x+","+temp.y+")"+" ");
}
return;
}
if (x<1 || x>n || y<1 || y>m)
return;
if (book[x][y] == 1)
return;
book[x][y] = 1;
stack.push(new NodePlumber(x, y));
if (a[x][y]>=5 && a[x][y]<=6) {
if (front == 1)
dfs(x,y+1,1);
if (front == 2)
dfs(x+1,y,2);
if (front == 3)
dfs(x,y-1,3);
if (front == 4)
dfs(x-1,y,4);
}
else if (a[x][y]>=1 && a[x][y]<=4) {
if (front == 1) {
dfs(x+1,y,2);
dfs(x-1,y,4);
}
if (front == 2) {
dfs(x,y+1,1);
dfs(x,y-1,3);
}
if (front == 3) {
dfs(x-1,y,4);
dfs(x+1,y,2);
}
if (front == 4) {
dfs(x,y+1,1);
dfs(x,y-1,3);
}
}
book[x][y] = 0;
stack.pop();
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
a[i][j] = sc.nextInt();
dfs(1,1,1);
if (flag == 0)
System.out.println("impossible");
sc.close();
}
}