题目链接
题目解析
注意: 由低到高来砍树, 砍了1然后去找2(最短路),砍了2去找3(最短路)...按照顺序把所有的树都砍完
算法原理
砍树的顺序
我们找到每俩个点的最短距离, 就相当于若干个迷宫问题
1. 砍树的顺序
把下标存在数组里面, 然后按照下标里面的值从小到大进行排序, 那么二维数组里面就存着待砍树的顺序了, 然后遍历二维数组, 就晓得从哪里砍到哪, 然后把所有的最小步数位置都累加起来, 如果走不到就返回-1.
代码编写
class Solution {
public int n, m;
public int cutOffTree(List<List<Integer>> forest) {
// 记录每一个元素的下标
n = forest.size();
m = forest.get(0).size();
List<int[]> treeIndex = new ArrayList<>();
// 把所有的元素放入
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (forest.get(i).get(j) > 1) {
treeIndex.add(new int[] { i, j });
}
}
}
// 把这些下标按照值从大到小进行排序
Collections.sort(treeIndex, (a, b) -> {
// a, b代表的是treeIndex里面的记录元素下标的数组, 是treeIndex的元素
return forest.get(a[0]).get(a[1]) - forest.get(b[0]).get(b[1]);
});
// 按照顺序来进行砍树
int rs = 0;// 用来记录最小步数的总步数
int bx = 0, by = 0;// 用来记录起始位置
// 遍历treeIndex
for (int[] x : treeIndex) {
// 记录终止位置
int ex = x[0], ey = x[1];
// 把起始和终止位置传入bfs进行最小值的查询
int step = bfs(forest, bx, by, ex, ey);
if (step == -1) {
// 遇到墙了
return -1;
}
rs += step;
// 更新起始位置
bx = ex;
by = ey;
}
return rs;
}
// 坐标数组
int[] dx = { 0, 0, -1, 1 };
int[] dy = { -1, 1, 0, 0 };
public int bfs(List<List<Integer>> forest, int bx, int by, int ex, int ey) {
// 起始等于终止, 直接返回即可
if (bx == ex && by == ey) {
return 0;
}
// 使用队列进行bfs
// 记录下标
Queue<int[]> q = new LinkedList<>();
// 记录每个下标是否第一次经过
boolean[][] judge = new boolean[n][m];
// 把起始下标先入队
q.add(new int[] { bx, by });
// 每次入队就修改状态
judge[bx][by] = true;
// 记录从起始到终止的步数
int step = 0;
while (!q.isEmpty()) {
// 记录每一层的大小
int sz = q.size();
step++;
for (int i = 0; i < sz; i++) {
// 出队
int[] tmp = q.poll();
int a = tmp[0];
int b = tmp[1];
// 进行上下左右
for (int j = 0; j < 4; j++) {
// 上下左右的下标
int x = a + dx[j];
int y = b + dy[j];
// 进行判断
if (x >= 0 && x < n && y >= 0 && y < m && forest.get(x).get(y) != 0 && !judge[x][y]) {
if (x == ex && y == ey) {
return step;
}
// 进行入队操作
q.add(new int[] { x, y });
// 修改状态
judge[x][y] = true;
}
}
}
}
// 说明有墙
return -1;
}
}
进行排序的构造, 对于对象而言要指定对什么进行排序
我们建立下标和值的关联
使用一个下标数组, 记录每个元素的下标
第二次刷
class Solution {
// 分解成多个迷宫问题
int n, m;
public int cutOffTree(List<List<Integer>> f) {
// 记录长宽
n = f.size();
m = f.get(0).size();
// 记录每一棵树的下标, 并且按照值从小到大进行排序
List<int[]> indexs = new ArrayList<>();
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// >1 代表是树
if (f.get(i).get(j) > 1) {
indexs.add(new int[] { i, j });
}
}
}
// 把indexs里面的下标按照数组的值来从小到大进行排序
Collections.sort(indexs, (a, b) -> {
return f.get(a[0]).get(a[1]) - f.get(b[0]).get(b[1]);
});
// 进行多个迷宫问题的bfs
int bex = 0; // 记录起始位置
int bey = 0;
int rs = 0;// 记录总步数
for (int[] x : indexs) {
int endx = x[0];
int endy = x[1];
int step = bfs(f, bex, bey, endx, endy);
// 对步数进行有效判断
if (step == -1) {
return -1;
}
rs += step;
// 更新起始下标
bex = endx;
bey = endy;
}
// 返回总步数
return rs;
}
// 坐标数组
int[] dx = { 0, 0, -1, 1 };
int[] dy = { -1, 1, 0, 0 };
public int bfs(List<List<Integer>> f, int bex, int bey, int endx, int endy) {
// 如果一开始be==ed就不需要bfs了
if (bex == endx && bey == endy) {
return 0;
}
// 使用队列来进行bfs
Queue<int[]> q = new LinkedList<>();
// 记录是否第一次遍历到这个位置
boolean[][] juege = new boolean[n][m];
// 把起始位置放入
q.add(new int[] { bex, bey });
// 修改状态的值
juege[bex][bey] = true;
int step = 0;// 记录用的最小的步数
// 进入bfs
while (!q.isEmpty()) {
step++;
// 记录层数
int sz = q.size();
// 每一层每一个元素同时向上下左右四个方向拓展
for (int i = 0; i < sz; i++) {
// 记录临时元素
int[] tmp = q.poll();
// 保存下标
int a = tmp[0];
int b = tmp[1];
// 上下左右四个方向进行拓展
for (int k = 0; k < 4; k++) {
// 计算下标
int x = a + dx[k];
int y = b + dy[k];
// 进行判断
if (x >= 0 && x < n && y >= 0 && y < m && !juege[x][y] && f.get(x).get(y) != 0) {
// 是不是终止位置
if (x == endx && y == endy) {
return step;
}
// 入队
q.add(new int[] { x, y });
// 修改状态
juege[x][y] = true;
}
}
}
}
// 说明有墙
return -1;
}
}
一开始进行判断不要漏了起始就是终止