本来是想拿这个题来练习双向bfs的 结果一开始连朴素写法都写不出来
终于坎坷地打完代码 所以来发个题解
双向bfs+map判重
如果不加双向会t一个点 加完就能ac
代码比较丑 大家将就看看吧
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <queue>
#include <map>//个人惯用的头文件
using namespace std;
struct xxx{
string str;
short i;
};//i表示0在串中的位置
bool check(int i,int d){//d是0移动的方向 详见下方dic数组
if((i + d > 8)||(i + d < 0)) return 0;
if((i % 3 == 0)&&(d == -1)) return 0;
if((i % 3 == 2)&&(d == 1)) return 0;
return 1;
}//分别判断0的位置进行变化后是否合法
int dic[4] = {1,-1,3,-3};//0变化的四种方向
map <string,int> m1,m2;
queue <xxx> q1,q2;//两个队列和map别用于两个bfs方向
xxx st,ed;//起始和终止状态存储
int c,c1 = 1,c2 = 1,cc = 0;//分别表示计数器、当前层级正方向的状态总数、当前层级负方向的状态总数、当前层级
int bfs(){
while (!q1.empty()){
++ cc;//更新当前层级
xxx lx,x;//lx用于取出队首 x用于变化操作
c = c1;
c1 = 0;
for (int i = 1;i <= c;++ i){//遍历当前层级正方向的所有状态
lx = q1.front();q1.pop();//取队头
for (int z = 0;z < 4;++ z){//四种移动方向
x = lx;//重置x
int d = dic[z];
if(!check(x.i,d)) continue;//判断移动是否合法
swap(x.str[x.i],x.str[x.i + d]);
x.i += d;//更新x的状态
if (m1[x.str]) continue;
q1.push(x);m1[x.str] = 1;++ c1;//判断是否重复 不重复时将新状态存入队 且更新c1
if (m2[x.str]) return cc * 2 - 1;//判断是否在上一层的反方向上出现过该状态 有则返回(当前层级+上一层层级)(即cc*2 - 1)
}
}
//-------------------------------以下是反方向的处理 与正方向相同
c = c2;
c2 = 0;
for (int i = 1;i <= c;++ i){
lx = q2.front();q2.pop();
for (int z = 0;z < 4;++ z){
x = lx;
int d = dic[z];
if(!check(x.i,d)) continue;
swap(x.str[x.i],x.str[x.i + d]);
x.i += d;
if (m2[x.str]) continue;
q2.push(x);m2[x.str] = 1;++ c2;
if (m1[x.str]) return cc * 2;//这里应为判断同层级的正方向是否出现过该状态 有则返回(当前层级+当前层级)
}
}
}
}
int main (){
cin >> st.str;
for (int i = 0;i < 9;++ i){
if(st.str[i] == '0') st.i = i;
}//读入 处理0的位置
ed = (xxx){"123804765",4};
q1.push(st);m1[st.str] = 1;
q2.push(ed);m2[st.str] = 1;//初始化
if(st == ed){
printf("0");
return 0;
}//特判是否只有一个状态
printf("%d",bfs());//进入bfs
return 0;
}