移动矩阵的问题求解方法

个人算法练习总结贴

Posted by Jeremy Song on 2022-06-05
Estimated Reading Time 4 Minutes
Words 964 In Total
Viewed Times

题目介绍

最近练题的过程中,遇到这么一种情况:在一个二维矩阵中,有一个小的固定的图形,需要移动这个小的图形到达某个指定的位置,求最短距离。

图形化的题目看起来长下面这个样子:

其中:

  • S:表示起始位置
  • O:表示目标位置,S 接触到 O 为终止条件
  • W:表示水域,是不可通过的区域

这个图还没看明白题目的话,不要紧。看下面这张图!!!

对滴!就是90坦克大战的简易版(暴露年龄,哈哈~),只不过我们题目的设定没那么复杂,坦克也不能转向。
下面就来看看代码吧!

代码实现

以下代码仅表述核心算法逻辑,请忽略变量初始化等问题哈~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;

/**
* 在一个二维矩阵中移动固定图案(一个小的矩阵,即多个元素的组合,其两两之间的相对位置不变)
* 求其到位某个满足条件位置的最小距离解法
*/
public class MoveArray {
// 矩阵规模,R行C列
static int R, C;
// 矩阵地图
static char[][] MAP;

// 待移动的图形的点的集合
static List<int[]> BODY;
// 上下左右移动的偏移量
static int[][] MOVE = new int[][]{{0, 1}, {0, -1}, {1, 0}, {-1, 0}};

static Queue<List<int[]>> QUEUE;
static List<int[]> NEXT_ITEMS;
static boolean[][] VISITED;

static int ANS;

/**
* 测试入口
*/
public static void main(String[] args) {
initData();
ANS = resolve();
System.out.println(ANS);
}

/**
* 初始化数据
*/
private static void initData() {
MAP = new char[R][C];
VISITED = new boolean[R][C];
QUEUE = new ArrayDeque<>();
NEXT_ITEMS = new ArrayList<>();
ANS = 0;

QUEUE.offer(BODY);
for (int[] cell : BODY) {
// 将指定的点的集合标记为已访问
VISITED[cell[0]][cell[1]] = true;
}
}

/**
* 求解过程(标准BFS过程)
* @return 最小距离(也可以直接用ANS,不用返回值)
*/
private static int resolve() {
while (!QUEUE.isEmpty()) {
ANS++;
while (!QUEUE.isEmpty()) {
List<int[]> body = QUEUE.poll();
for (int[] shift : MOVE) {
boolean[] result = how(body, shift);
if (!result[0]) {
// 当前偏移不可达,继续下一个偏移检查
continue;
}
if (result[1]) {
// 终止条件达成,返回当前距离(最小距离)
return ANS;
}
// 当前偏移可通过,加入下一次偏移的集合
List<int[]> moved = new ArrayList<>();
for (int[] cell : body) {
int nx = cell[0] + shift[0];
int ny = cell[1] + shift[1];

// 添加新的偏移后的点
moved.add(new int[]{nx, ny});
// 设置新的偏移点未已访问
VISITED[nx][ny] = true;
}
NEXT_ITEMS.add(moved);
}
}
QUEUE.addAll(NEXT_ITEMS);
NEXT_ITEMS.clear();
}
return -1;
}

private static boolean[] how(List<int[]> body, int[] shift) {
// 是否可达标记
boolean couldGo = false;
// 是否满足终止条件标记
boolean touch = false;

for (int[] cell : body) {
int nx = cell[0] + shift[0];
int ny = cell[1] + shift[1];
if (nx >= 0 && nx < R && ny >= 0 && ny < C) {
// 偏移的点在MAP范围内,进一步判断
if ('O' == MAP[nx][ny]) {
// 满足终止条件,变更标记
touch = true;
}
if ('W' == MAP[nx][ny]) {
// 偏移后的点不可达,修改可达标记,跳出
// 因为下一个if的判断,此处必须修改可达标记!
couldGo = false;
break;
}
if (!VISITED[nx][ny]) {
// 任意一个偏移后的点未访问,则认为此次偏移后的整体未访问过,修改可达标记
couldGo = true;
}
} else {
// 偏移的点在MAP范围外,该偏移不可达,跳出
couldGo = false;
break;
}
}

return new boolean[]{couldGo, touch};
}
}

最后

以上算法就是对此类问题的个人理解,当然这个算法思路在其他类似的模型中也能适用。如果您有更好的解法思路,请留言交流哈~


欢迎关注我的公众号 须弥零一,跟我一起学习IT知识。


如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !