题目大意:
最小化修改的格子的数量,使得人物能从左上角走到右下角
思路:
在每一个位置上有改与不改两种选择,也就是 0 和 1 两种花费,要是花费最少,我们可以使用 01bfs 解决。
01bfs 本质上就是贪心的思想,运用双端队列,将花费为 0 的从队首加入,花费为 1 的从队尾加入,这样每次出队的节点都是当前这一步的最优节点。
在本题中需要记录更改的路径,因为每个节点只会被更新一次,那么我们记录其前驱节点,即可从终点逆推回去。
队列中储存了当前点、上一点和修改次数,点的存储尽量采用 0-index 为标号的存储方式,易于存储且容易转为为二维坐标。以下使用了 deque<tuple<int, int, int>>
的结构作为队列,实际上,对于参数类型相同的情况,我们可以使用 array<int, 3>
,这样更加易于访问。
还有一个问题是,我一直采用 vscode 编写代码,其中 __cplusplus
的值没有办法更新,一直显示为 199711L ,所以导致没有 #include<array>
,没法找到关于 array 的构造函数,没有找到具体的命令行参数解决这个问题,现在就是编译运行没有任何问题,但是语法检查会带波浪线。
Code:
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
const string ch = "LRUD";
int T;
cin >> T;
while (T--) {
int n, m;
cin >> n >> m;
vector<string> mp(n);
for (int i = 0; i < n; i++) {
cin >> mp[i];
}
vector<int> pre(n * m, -1), dis(n * m, -1);
deque<tuple<int, int, int>> q; // 当前点 上一点 修改次数
q.push_front(tuple<int, int, int>(0, -1, 0));
while (!q.empty()) {
auto now = q.front();
q.pop_front();
if (dis[get<0>(now)] != -1) { // 确保每个点只更新一次,即得到最优解。一个点最多进队4或8次。
continue;
}
dis[get<0>(now)] = get<2>(now);
pre[get<0>(now)] = get<1>(now); // 只用记录到达每个点时的状态
int nx = get<0>(now) / m, ny = get<0>(now) % m;
for (int i = 0; i < 4; i++) {
int tx = nx + dir[i][0], ty = ny + dir[i][1];
int nxt = tx * m + ty;
if (tx < 0 || tx > n - 1 || ty < 0 || ty > m - 1)
continue;
if (ch[i] != mp[nx][ny]) {
q.push_back({nxt, get<0>(now), get<2>(now) + 1});
} else {
q.push_front({nxt, get<0>(now), get<2>(now)});
}
}
}
cout << dis[n * m - 1] << "\n";
int x = n - 1, y = m - 1;
while (!(x == 0 && y == 0)) {
int px = pre[x * m + y] / m, py = pre[x * m + y] % m;
char td;
for (int i = 0; i < 4; i++) {
int tx = px + dir[i][0], ty = py + dir[i][1];
if (tx == x && ty == y) {
td = ch[i];
break;
}
}
if (td != mp[px][py]) {
cout << px + 1 << " " << py + 1 << " " << td << "\n";
}
x = px, y = py;
}
}
return 0;
}