2024年3月27号题解
Open the Lock
解题思路
- 很明显从起点到终点的距离等于终点到起点的距离,那么它就是一个无向图
- 那么我们可以使用dbfs来进行剪枝,来优化我们的搜索
代码实现
#define _CRT_SECURE_NO_WARNINGS
#include <sstream>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_map>
#include<queue>
#include <set>
#define sc scanf
#define pr printf
using namespace std;
const int M = 10000;//最多10000种状态
int t;//样例个数
int q1[M];//从起点遍历的队列
int q2[M];//从终点遍历的队列
int l1;//q1的队列头
int l2;//q2的队列头
int r1;//q1的队列尾
int r2;//q2的队列尾
bool v1[M];//起点出发访问过的状态
bool v2[M];//从终点出发访问过的状态
int a[4];//用来存放状态
int b[4];//暂时存放状态
int getStatus(int a[]) {//给定一个数组返回它对应的状态
int ans = a[0] * 1000 + a[1] * 100 + a[2] * 10 + a[3];
return ans;
}
void dbfs()//双向bfs
{
int s1 = 0;//起点状态
int s2 = 0;//终点状态
int level1 = 0;//从起点出发的层数
int level2 = 0;//从终点出发的层数
memset(v1, 0, sizeof(v1));//给v1数组清0
memset(v2, 0, sizeof(v2));//给v2数组清0
r2 = r1 = l2 = l1 = 0;
sc("%d", &s1);//读入起点的状态
sc("%d", &s2);//读入终点的状态
v1[s1] = 1;//标记起点状态已访问
v2[s2] = 1;//标记终点状态已访问
q1[r1++] = s1;//把起点加入起点队列
q2[r2++] = s2;//把终点加入终点队列
while (l1 < r1 && l2 < r2) {//只要两个队列没有空
int size1 = r1 - l1;//计算起点队列中的元素个数
int size2 = r2 - l2;//计算终点队列种的元素个数
if (size1 <= size2) {//如果起点队列中的元素个数更少
for (int i = 0; i < size1; i++) {//遍历起点队列
s1 = q1[l1++];//出队
if (v2[s1]) {//如果在终点队列中访问过的状态出现过,代表这条路径可以从终点到起点
pr("%d\n", level1 + level2);
return;//第一次就是最短路径
}
//取出对应的状态
b[0] = a[0] = s1 / 1000;
b[1] = a[1] = s1 / 100 % 10;
b[2] = a[2] = s1 / 10 % 10;
b[3] = a[3] = s1 % 10;
//+1
for (int j = 0; j < 4; j++) {
if (b[j] == 9) {
b[j] = 1;
int t = getStatus(b);
if (!v1[t]) {
v1[t] = 1;
q1[r1++] = t;
}
b[j] = a[j];
}
else {
b[j] += 1;
int t = getStatus(b);
if (!v1[t]) {
v1[t] = 1;
q1[r1++] = t;
}
b[j] = a[j];
}
}
// -1
for (int j = 0; j < 4; j++) {
if (b[j] == 1) {
b[j] = 9;
int t = getStatus(b);
if (!v1[t]) {
v1[t] = 1;
q1[r1++] = t;
}
b[j] = a[j];
}
else {
b[j] -= 1;
int t = getStatus(b);
if (!v1[t]) {
v1[t] = 1;
q1[r1++] = t;
}
b[j] = a[j];
}
}
//swap
for (int j = 0; j < 3; j++) {
swap(b[j], b[j + 1]);
int t = getStatus(b);
if (!v1[t]) {
v1[t] = 1;
q1[r1++] = t;
}
swap(b[j], b[j + 1]);
}
}
level1++;//层数加一
}
else {
for (int i = 0; i < size2; i++) {//遍历终点队列
s1 = q2[l2++];//出队
if (v1[s1]) {//如果终点访问过的状态起点访问过了,那么这条路就是最短路满足要求
pr("%d\n", level1 + level2);
return;
}
//取出对应的状态
b[0] = a[0] = s1 / 1000;
b[1] = a[1] = s1 / 100 % 10;
b[2] = a[2] = s1 / 10 % 10;
b[3] = a[3] = s1 % 10;
//+1
for (int j = 0; j < 4; j++) {
if (b[j] == 9) {
b[j] = 1;
int t = getStatus(b);
if (!v2[t]) {
v2[t] = 1;
q2[r2++] = t;
}
b[j] = a[j];
}
else {
b[j] += 1;
int t = getStatus(b);
if (!v2[t]) {
v2[t] = 1;
q2[r2++] = t;
}
b[j] = a[j];
}
}
// -1
for (int j = 0; j < 4; j++) {
if (b[j] == 1) {
b[j] = 9;
int t = getStatus(b);
if (!v2[t]) {
v2[t] = 1;
q2[r2++] = t;
}
b[j] = a[j];
}
else {
b[j] -= 1;
int t = getStatus(b);
if (!v2[t]) {
v2[t] = 1;
q2[r2++] = t;
}
b[j] = a[j];
}
}
//swap相邻元素
for (int j = 0; j < 3; j++) {
swap(b[j], b[j + 1]);//交换
int t = getStatus(b);
if (!v2[t]) {
v2[t] = 1;
q2[r2++] = t;
}
swap(b[j], b[j + 1]);//再交换回来,就恢复原装了
}
}
level2++;//层数加一
}
}
}
int main() {
sc("%d", &t);
while (t--) {
dbfs();
}
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步