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;
}