kuangbin-Acwing系列文章,记录了自己在练习过程中的想法和体会,可能不具有太大价值,大家选择性阅读。
kuangbin-Acwing
一、棋盘问题(DFS-简单)

因为放了一行,那当前行就不能放了,可以以行为元素进行搜索,每次遍历当前行的所有列是否可以放,用数组记录列的使用情况。 需要注意可能存在当前行不放、当前列不放的情况。
import java.util.*;
public class Main {
static int n;
static char[][] map;
static int count;
static boolean[] col;
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
while (true) {
n = scan.nextInt();
int k = scan.nextInt();
if (n == -1 && k == -1) break;
map = new char[n][n];
for (int i = 0; i < n; i++) {
map[i] = scan.next().toCharArray();
}
count = 0;
col = new boolean[n];
dfs(0, k);
System.out.println(count);
}
}
public static void dfs(int row, int k) {
if (k == 0) {
count++;
return;
}
if (row == n) return;
// 搜索每一列
for (int i = 0; i < n; i++) {
// 判断当前列是否可用
if (!col[i] && map[row][i] == '#') {
col[i] = true;
dfs(row + 1, k - 1);
// 回溯
col[i] = false;
}
}
// 也可以当前行不放东西
dfs(row + 1, k);
}
}
二、地牢大师(BFS-简单)

考虑三维的BFS即可,还记得BFS模板的两种写法吗?一种直接把当前队列中的所有元素拿出来,一种是边遍历边入队。
import java.util.*;
class node {
int x, y, z;
node(){};
node(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
}
public class Main {
static char[][][] map;
// 方向数组
static int[] xx = new int[] {1, 0, -1, 0, 0, 0};
static int[] yy = new int[] {0, 1, 0, -1, 0, 0};
static int[] zz = new int[] {0, 0, 0, 0, -1, 1};
static boolean[][][] vis;
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
while (true) {
int L = scan.nextInt();
int R = scan.nextInt();
int C = scan.nextInt();
if (L == 0 && R == 0 && C == 0) break;
map = new char[L][R][C];
vis = new boolean[L][R][C];
node b = new node();
for (int i = 0; i < L; i++) {
for (int j = 0; j < R; j++) {
map[i][j] = scan.next().toCharArray();
for (int k = 0; k < C; k++) {
if (map[i][j][k] == 'S') {
b = new node(i, j, k);
}
}
}
}
// 是否到达终点
boolean flag = false;
Queue<node> queue = new LinkedList<>();
queue.offer(b);
int count = 0;
while (!queue.isEmpty()) {
if (flag) break;
int size = queue.size();
// 遍历当前队列中所有节点
for (int i = 0; i < size; i++) {
node tmp = queue.poll();
vis[tmp.x][tmp.y][tmp.z] = true;
// 遍历6个方向
for (int j = 0; j < 6; j++) {
node temp = new node();
temp.x = tmp.x + xx[j];
temp.y = tmp.y + yy[j];
temp.z = tmp.z + zz[j];
if (temp.x < 0 || temp.y < 0 || temp.z < 0 || temp.x >= L || temp.y >= R || temp.z >= C) {
continue;
}
if (map[temp.x][temp.y][temp.z] == '#') continue;
if (vis[temp.x][temp.y][temp.z]) continue;
if (map[temp.x][temp.y][temp.z] == 'E') {
flag = true;
count++;
break;
}
// 新节点入队
vis[temp.x][temp.y][temp.z] = true;
queue.offer(temp);
}
if (flag) {
break;
}
}
// 完成整个一波的前进
count++;
}
if (flag) {
System.out.printf("Escaped in %d minute(s).\n", count - 1);
} else {
System.out.println("Trapped!");
}
}
}
}
三、抓住那头牛(BFS-简单)

属于是一维的BFS,每次遍历三个可能的移动轨迹即可。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int k = scan.nextInt();
boolean[] vis = new boolean[100001];
Queue<Integer> queue = new LinkedList<>();
queue.offer(n);
boolean flag = false;
int count = 0;
// 每次三种移动方式
int[] step = new int[3];
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
int cur = queue.poll();
vis[cur] = true;
step[0] = cur - 1;
step[1] = cur + 1;
step[2] = cur * 2;
for (int j = 0; j < 3; j++) {
if (step[j] < 0 || step[j] > 100000) continue;
if (vis[step[j]]) continue;
if (step[j] == k) {
flag = true;
break;
}
vis[step[j]] = true;
queue.offer(step[j]);
}
if (flag) {
break;
}
}
// 整体遍历完一波
count++;
if (flag) {
break;
}
}
System.out.println(count);
}
}
四、费解的开关(状态压缩枚举-中等)

给你一个5x5的01矩阵,其中1代表灯on,0代表灯down,每次可以对某个位置的开关以及其上下左右的开关进行操作,使得它们的状态均改变,问能否只操作6个以内的开关使得全部的灯都亮。
属于搜索问题中的开关问题,本质是枚举,但不是对所有的开关都进行枚举。
解决思路: 1、枚举第一行的开关的所有操作情况,每个灯可以亮或者不亮,一行有5个灯,一共2 ^ 5种操作,这种很明显就是之前提到的状态压缩。
2、当我们发现对第一行进行了如上操作后,第二行的操作就固定了,只能去点亮第一行没亮的灯的下面的灯,因为第一行的其它灯的状态已经确定了,不能再去修改了,只能靠下面的灯的按动改变。,依次类推,第3,4行同样的操作,第5行是最后一行,不需要再进行操作,因为在处理第4行时,就按了第5行。
3、怎么判断全部都点亮了? 最后看一下最后一行的情况,如果最后一行刚好全部点亮,说明是可行的方案,如果最后一行最后有灯没有点亮,说明这种方案不行。还要注意方案数最后要在<=6的范围内。
注意题目中需要对数组进行复制和还原,要注意java的数组复制方式
import java.util.*;
public class Main {
static char[][] map = new char[5][5];
static int n;
static int[] xx = new int[]{0,1,-1,0,0};
static int[] yy = new int[]{0,0,0,1,-1};
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
while (n != 0) {
int ans = Integer.MAX_VALUE;
// 读取当前状态
for (int i = 0; i < 5; i++) {
map[i] = scan.next().toCharArray();
}
// 确定第一行所有开关的可能状态
// 1就是把当前开关按一次
for (int i = 0; i < (1 << 5); i++) {
// 备份一份原始状态(注意java的数组复制方式)
char[][] backUp = new char[5][5];
for (int j = 0; j < 5; j++) {
backUp[j] = Arrays.copyOf(map[j], 5);
}
int step = 0;
// 看第一行哪些需要按
for (int j = 0; j < 5; j++) {
// 说明当前按钮要被按一次(不管亮不亮)
// 通过移位判断最低位是否为1
if (((i >> j) & 1) == 1) {
turn(0, j);
step++;
}
}
// 只需要看前4行
for (int j = 0; j < 4; j++) {
// 5列
for (int k = 0; k < 5; k++) {
// 如果有没亮的,就去按动它下面一个按钮
if (map[j][k] == '0') {
step++;
turn(j + 1, k);
}
}
}
// 遍历完前4行,看最后一行是否全亮
boolean flag = true;
for (int j = 0; j < 5; j++) {
if (map[4][j] == '0') {
flag = false;
break;
}
}
if (flag) {
ans = Math.min(ans, step);
}
// 恢复初始状态
map = backUp;
}
if (ans > 6) System.out.println(-1);
else System.out.println(ans);
n--;
}
}
public static void turn(int x, int y) {
for (int i = 0; i < 5; i++) {
int tx = x + xx[i];
int ty = y + yy[i];
if (tx < 0 || ty < 0 || tx >= 5 || ty >= 5) continue;
// 异或
map[tx][ty] ^= 1;
}
}
}
五、翻转(同上)

和上面方法一样,注意这两道题的本质还是状态压缩,还是要看数据量大小,如果过大肯定不能用。
import java.util.*;
public class Main {
static int[][] map;
static int m, n;
static int[] xx = new int[]{0,1,-1,0,0};
static int[] yy = new int[]{0,0,0,1,-1};
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
m = scan.nextInt();
n = scan.nextInt();
map = new int[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
map[i][j] = scan.nextInt();
}
}
int ans = Integer.MAX_VALUE;
int[][] ansMet = new int[m][n];
// 第一行可能的操作方案数:2 * 2 * 2... = 2 ^ 15
// 遍历所有操作情况
for (int i = 0; i < (1 << n); i++) {
// 备份状态一定要写在循环内,因为后面的恢复是赋予索引
// 备份初始状态
int[][] backUp = new int[m][n];
for (int j = 0; j < m; j++) {
backUp[j] = Arrays.copyOf(map[j], n);
}
// 记录整体翻转次数
int[][] cntMet = new int[m][n];
int step = 0;
// n列
for (int j = 0; j < n; j++) {
// 看具体第一行哪一列需要flip
if (((i >> j) & 1) == 1) {
cntMet[0][j]++;
flip(0, j);
step++;
}
}
// 遍历前m - 1行,最后再确定最后一行是否全0即可
for (int j = 0; j < m - 1; j++) {
// 每行n列
for (int k = 0; k < n; k++) {
// 当前行为1,就把下面的一个元素翻转
if (map[j][k] == 1) {
cntMet[j + 1][k]++;
flip(j + 1, k);
step++;
}
}
}
boolean flag = true;
for (int j = 0; j < n; j++) {
if (map[m - 1][j] == 1) {
flag = false;
break;
}
}
if (flag && step < ans) {
ans = step;
// 记录翻转次数数组
for (int j = 0; j < m; j++) {
ansMet[j] = Arrays.copyOf(cntMet[j], n);
}
}
// 恢复初始状态
map = backUp;
}
if (ans == Integer.MAX_VALUE) {
System.out.println("IMPOSSIBLE");
} else {
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
System.out.print(ansMet[i][j] + " ");
}
System.out.println();
}
}
}
public static void flip(int x, int y) {
for (int i = 0; i < 5; i++) {
int tx = x + xx[i];
int ty = y + yy[i];
if (tx < 0 || ty < 0 || tx >= m || ty >= n) continue;
// 异或
map[tx][ty] ^= 1;
}
}
}
六、找倍数(BFS-简单)

思路很简单,就是尝试当前位置0-1情况,关键是如何把代码写的足够美观简洁。题目中说任意方案都行,那我们就用BFS最快找到满足条件的即可。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
while (true) {
int n = scan.nextInt();
if (n == 0) break;
Queue<Long> queue = new LinkedList<>();
queue.offer(1L);
while (!queue.isEmpty()) {
long cur = queue.poll();
if (cur % n == 0) {
System.out.println(cur);
break;
}
// 就这两种情况
queue.offer(cur * 10);
queue.offer(cur * 10 + 1);
}
}
}
}
七、质数路径(BFS-简单)

刚开始是的思路是找最近的且相差只有一个数字的素数,但是速度很慢,可以换种思路:对于每个数只有4位,每位有10种更换的可能,所以总共每个数有40种可能,去除特殊情况(最高位=0,或与原数相同),并用vis数组记录使用过的素数(注意每次新的数据需要清空vis数组)。
再用一个distTo数组记录起点素数到某个数的操作次数。
btw,线性筛一定要熟练。
import java.util.*;
public class Main {
public static int[] prime = new int[10001];
public static boolean[] isPrime = new boolean[10001];
public static boolean[] vis;
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
// 线性筛素数
findPrime();
while ((n--) > 0) {
int a = scan.nextInt();
int b = scan.nextInt();
if (a == b) {
System.out.println(0);
} else {
// 别忘了重置
vis = new boolean[10001];
// 最少次数BFS
Queue<Integer> queue = new LinkedList<>();
// 记录开始素数到某个素数的步数
int[] distTo = new int[10001];
distTo[a] = 0;
queue.offer(a);
boolean flag = false;
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
int cur = queue.poll();
vis[cur] = true;
// 四个位置,可以换成0-9,共40种情况
for (int j = 0; j < 10; j++) {
for (int k = 0; k < 4; k++) {
// 最高位不能为0
if (j == 0 && k == 0) continue;
int tmp = change(cur, j, k);
// 与自身相同不行
if (tmp == cur) continue;
// 已经遍历过的数
if (vis[tmp]) continue;
// 标记已访问
vis[tmp] = true;
if (tmp == b) {
distTo[tmp] = distTo[cur] + 1;
flag = true;
break;
}
// 是素数
if (isPrime[tmp] == false) {
queue.offer(tmp);
distTo[tmp] = distTo[cur] + 1;
}
}
if (flag) break;
}
}
if (flag) break;
}
System.out.println(distTo[b]);
}
}
}
// 改变具体某一位
public static int change(int old, int num, int site) {
// site = 0修改高位,size = 3修改最低位
int a = old % 10;
int b = old / 10 % 10;
int c = old / 100 % 10;
int d = old / 1000 % 10;
if (site == 0) {
d = num;
} else if (site == 1) {
c = num;
return d * 1000 + c * 100 + b * 10 + a;
} else if (site == 2) {
b = num;
} else {
a = num;
}
return d * 1000 + c * 100 + b * 10 + a;
}
// 线性筛
public static void findPrime() {
int count = 0;
for (int i = 2; i < 10000; i++) {
if (isPrime[i] == false) {
prime[count++] = i;
}
// 除去合数
for (int j = 0; j < count; j++) {
if (prime[j] * i > 10000) break;
isPrime[prime[j] * i] = true;
// 保证每个合数只被最小的质因子分解
if (i % prime[j] == 0) break;
}
}
}
}
八、洗牌(BFS-简单)


模拟一下洗牌的过程,把每次洗牌洗好的结果放入队列中,用set记录当前洗牌结果是否被遍历过,直至最后找到相同的洗牌结果。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int t = scan.nextInt();
int index = 0;
while (index < t) {
int c = scan.nextInt();
char[] s1 = scan.next().toCharArray();
char[] s2 = scan.next().toCharArray();
String s3 = scan.next();
StringBuilder str = new StringBuilder();
// 先完成第一次洗牌,因为后续只需要把洗好的牌拆成两半再洗
for (int i = 0; i < c; i++) {
// S2在最底下
str.append(s2[i]);
str.append(s1[i]);
}
// 开始洗牌结果入队
Queue<String> queue = new LinkedList<>();
queue.offer(str.toString());
Set<String> set = new HashSet<>();
set.add(str.toString());
// 记录洗牌次数
int count = 1;
boolean flag = false;
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
char[] tmp = queue.poll().toCharArray();
StringBuilder newStr = new StringBuilder();
// 再洗牌
for (int j = 0; j < c; j++) {
// 上半部分是新的s2
newStr.append(tmp[j + c]);
newStr.append(tmp[j]);
}
if (set.contains(newStr.toString())) continue;
if (newStr.toString().equals(s3)) {
flag = true;
break;
}
set.add(newStr.toString());
// 入队
queue.offer(newStr.toString());
}
// 整个完成一波
count++;
if (flag) {
break;
}
}
if (flag) {
System.out.printf("%d %d\n", index + 1, count);
} else {
System.out.printf("%d %d\n", index + 1, -1);
}
index++;
}
}
}
九、罐子(BFS-简单)

有三种操作,每种操作又有两种情况,共有六种情况。需要把ab容器的容量共同用于判断是否访问过,即二维数组vis。
注意class,StringBuilder,数组等,在赋值时出现引用的情况,这会很容易导致结果错误,因为后续的操作会改变已经保存下来的结果。
import java.util.*;
class node {
int a, b;
StringBuilder sb;
node(){};
node(int a, int b, StringBuilder sb) {
this.a = a;
this.b = b;
this.sb = sb;
}
}
public class Main {
public static int a, b;
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
// a b是容积
a = scan.nextInt();
b = scan.nextInt();
int c = scan.nextInt();
int count = 0;
// 把 a、b容量共同用来判断是否vis
boolean[][] vis = new boolean[101][101];
Queue<node> queue = new LinkedList<>();
// 开始情况容量都为0,入队
queue.offer(new node(0, 0, new StringBuilder()));
vis[0][0] = true;
boolean flag = false;
StringBuilder ans = new StringBuilder();
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
// 遍历三种操作,每种操作有两种可能,共6种结果
node temp = queue.poll();
vis[temp.a][temp.b] = true;
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 2; k++) {
node tmp = work(j, temp.a, temp.b, new StringBuilder(temp.sb), k);
if (vis[tmp.a][tmp.b]) continue;
vis[tmp.a][tmp.b] = true;
// 满足题目条件
if (tmp.a == c || tmp.b == c) {
flag = true;
ans = new StringBuilder(tmp.sb);
break;
}
queue.offer(tmp);
}
if (flag) break;
}
}
count++;
if (flag) break;
}
if (flag) {
System.out.println(count);
System.out.println(ans.toString());
} else {
System.out.println("impossible");
}
}
public static node work(int index, int aa, int bb, StringBuilder sb, int cnt) {
int ta = aa;
int tb = bb;
if (index == 0) {
// 装满水
if (cnt == 0) {
ta = a;
return new node(ta, tb, sb.append("FILL(1)\n"));
} else {
tb = b;
return new node(ta, tb, sb.append("FILL(2)\n"));
}
} else if (index == 1) {
// 倒出水
if (cnt == 0) {
ta = 0;
return new node(ta, tb, sb.append("DROP(1)\n"));
} else {
tb = 0;
return new node(ta, tb, sb.append("DROP(2)\n"));
}
} else {
// 把 a 倒入 b
if (cnt == 0) {
int need = b - tb;
int have = ta;
if (need != 0 && have != 0) {
if (have < need) {
ta = 0;
tb += have;
} else {
ta -= need;
tb = b;
}
}
return new node(ta, tb, sb.append("POUR(1,2)\n"));
} else {
int need = a - ta;
int have = tb;
if (need != 0 && have != 0) {
if (have < need) {
tb = 0;
ta += have;
} else {
tb -= need;
ta = a;
}
}
return new node(ta, tb, sb.append("POUR(2,1)\n"));
}
}
}
}
十、点火游戏(BFS-简单)


想法很简单,记录下所有草地的坐标和草地的块数,暴力枚举两个草地点燃,如果草地只有一块直接打印0。
import java.util.*;
class node {
int x, y;
node(){};
node(int x, int y) {
this.x = x;
this.y = y;
}
}
public class Main {
// 方向数组
static int[] x = new int[] {-1,1,0,0};
static int[] y = new int[] {0,0,-1,1};
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int t = scan.nextInt();
int index = 0;
int n, m;
char[][] map;
boolean[][] vis;
while (index < t) {
n = scan.nextInt();
m = scan.nextInt();
map = new char[n][m];
for (int i = 0; i < n; i++) {
map[i] = scan.next().toCharArray();
}
// 记录所有草地下标
int cnt = 0;
int[] bx = new int[n * m];
int[] by = new int[n * m];
// 记录草地面积
int originGrass = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (map[i][j] == '#') {
bx[cnt] = i;
by[cnt++] = j;
originGrass++;
}
}
}
// 特别情况:一个草地
if (originGrass == 1) {
System.out.printf("Case %d: %d\n", index + 1, 0);
} else {
// 暴力枚举点燃两个草地
boolean flag = false;
int ans = Integer.MAX_VALUE;
for (int i = 0; i < cnt; i++) {
node a = new node(bx[i], by[i]);
for (int j = i + 1; j < cnt; j++) {
Queue<node> queue = new LinkedList<>();
node b = new node(bx[j], by[j]);
queue.offer(a);
queue.offer(b);
// 每一次都要清空vis数组
vis = new boolean[n][m];
vis[bx[i]][by[i]] = true;
vis[bx[j]][by[j]] = true;
// 当前情况下所需时间
int count = 0;
// 当前情况下点燃的草地面积
int grassCnt = 2;
while (!queue.isEmpty()) {
int size = queue.size();
for (int k = 0; k < size; k++) {
node tmp = queue.poll();
vis[tmp.x][tmp.y] = true;
for (int l = 0; l < 4; l++) {
int tx = tmp.x + x[l];
int ty = tmp.y + y[l];
if (tx < 0 || ty < 0 || tx >= n || ty >= m || vis[tx][ty] || map[tx][ty] != '#') {
continue;
}
vis[tx][ty] = true;
grassCnt++;
queue.offer(new node(tx, ty));
}
}
// 全体都完成一波,时间++
count++;
}
// 全部点燃了
if (grassCnt == originGrass) {
flag = true;
ans = Math.min(ans, count);
}
}
}
// 当前一个case输入完成
if (flag) {
System.out.printf("Case %d: %d\n", index + 1, ans - 1);
} else {
System.out.printf("Case %d: %d\n", index + 1, -1);
}
}
index++;
}
}
}
十一、起火迷宫(两次BFS-简单)

先用一次BFS记录火源到达各个可能点的时间,再用一次BFS去遍历乔可能走的点,能够到达该点的条件是:不是障碍、且到达该点的时间严格大于火源烧到这里的时间,只要找到有一次情况能到达出口即可break。注意判断特别情况:即一开始就在边缘位置,只用走一步即可出去。
import java.util.*;
class node {
int x, y;
node(){};
node(int x, int y) {
this.x = x;
this.y = y;
}
}
public class Main {
static int[] x = new int[] {1,-1,0,0};
static int[] y = new int[] {0,0,1,-1};
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int t = scan.nextInt();
char[][] map;
// 记录火烧到可能地方的时间
int[][] time;
// 记录J跑到可能地方的时间
int[][] run;
while ((t--) > 0) {
int r = scan.nextInt();
int c = scan.nextInt();
map = new char[r][c];
time = new int[r][c];
for (int i = 0; i < r; i++) {
Arrays.fill(time[i], -1);
}
node b = new node();
Queue<node> fireTime = new LinkedList<>();
for (int i = 0; i < r; i++) {
map[i] = scan.next().toCharArray();
for (int j = 0; j < c; j++) {
if (map[i][j] == 'J') {
b = new node(i, j);
} else if (map[i][j] == 'F') {
fireTime.offer(new node(i, j));
// 火烧到这里的时间 = 0
time[i][j] = 0;
}
}
}
// 求得每个可能位置,火烧到的时间
while (!fireTime.isEmpty()) {
int size = fireTime.size();
for (int i = 0; i < size; i++) {
node tp = fireTime.poll();
for (int j = 0; j < 4; j++) {
int tx = tp.x + x[j];
int ty = tp.y + y[j];
if (tx < 0 || tx >= r || ty < 0 || ty >= c || time[tx][ty] != -1) continue;
if (map[tx][ty] == '#') continue; // 障碍物烧不到
// 火烧到这里的时间
time[tx][ty] = time[tp.x][tp.y] + 1;
fireTime.offer(new node(tx, ty));
}
}
}
// 再看起点到达各个可能点的时间是否绝对小于火烧到这里的时间
// 本来就处在边缘位置
if (b.x == 0 || b.x == r - 1 || b.y == 0 || b.y == c - 1) {
System.out.println(1);
} else {
run = new int[r][c];
for (int i = 0; i < r; i++) {
Arrays.fill(run[i], -1);
}
run[b.x][b.y] = 0;
Queue<node> person = new LinkedList<>();
person.offer(b);
// 是否有路可以走
int ans = 0;
boolean flag = false;
while (!person.isEmpty()) {
int size = person.size();
for (int i = 0; i < size; i++) {
node tp = person.poll();
for (int j = 0; j < 4; j++) {
int tx = tp.x + x[j];
int ty = tp.y + y[j];
if (tx < 0 || tx >= r || ty < 0 || ty >= c || run[tx][ty] != -1) continue;
if (map[tx][ty] == '#') continue; // 人不能走到这里
run[tx][ty] = run[tp.x][tp.y] + 1;
// 跑到这里的时间大于等于火烧到的时间
if (run[tx][ty] >= time[tx][ty]) {
continue;
}
// 跑出去了
if (tx == 0 || tx == r - 1 || ty == 0 || ty == c - 1) {
ans = run[tx][ty] + 1;
flag = true;
break;
}
person.offer(new node(tx, ty));
}
if (flag) break;
}
if (flag) break;
}
if (flag) System.out.println(ans);
else System.out.println("IMPOSSIBLE");
}
}
}
}
其余的搜索题目都比较简单了,都可以套用上面的方法求解,就不再列举。
本文是kuangbin的Acwing算法系列文章,详细介绍了使用DFS和BFS解决一系列编程问题,包括棋盘问题、地牢大师、抓住那头牛、费解的开关、翻转、找倍数、质数路径、洗牌、罐子、点火游戏和起火迷宫等。每道题目通过实例解析了搜索算法的应用,涉及状态压缩、数组复制、队列操作等技巧。
1366

被折叠的 条评论
为什么被折叠?



