洛谷P1379八数码题解
这种题目显然是个\(bfs\)。(因为要求最小步数)
每次枚举0向哪移动,用\(map\)判重即可大佬都是用康托展开或者是hash,但是我不会
用\(map\)是因为转成9位数之后开\(bool\)数组存不下会hash和康托展开的大佬当我在放屁
单向\(bfs\):
int X[9]={1,1,1,2,2,2,3,3,3},Y[9]={1,2,3,1,2,3,1,2,3};//数转矩阵用的
int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};//0的移动方向
int hy[4][4]=//矩阵的位置对应的数的位置
{{0,0,0,0},
{0,0,1,2},
{0,3,4,5},
{0,6,7,8}
};
string k;
struct qwq{
string u;//这里的9位数我用字符串存的
int bs,lig;//bs记录步数,lig记录0的位置
};
queue<qwq> q;
map<int,bool> usd;
int main()
{
cin>>k;int lig;
for(int i=0;i<9;i++)
if(k[i]=='0') {lig=i;break;}
qwq a;
a.u=k;a.bs=0;a.lig=lig;
q.push(a);
while(!q.empty())
{
qwq b=q.front();
q.pop();
string mbl=b.u;int qmf=0;
for(int i=0;i<9;i++)
qmf=qmf*10+mbl[i]-48;
if(qmf==123804765) {//是否到达最终状态
printf("%d",b.bs);break;
}
int lg=b.lig;
for(int i=0;i<4;i++)
{
int xx=X[lg]+dx[i],yy=Y[lg]+dy[i];
if(xx<1||xx>3||yy<1||yy>3) continue;
qwq nw;
nw=b;
int e=b.lig,f=hy[xx][yy];
nw.lig=f;nw.u[e]=b.u[f];nw.u[f]=b.u[e];//这一坨是交换0和目标位置的数(手写swap)
nw.bs=b.bs+1;
int pd=0;string pp=nw.u;//计算当前的数
for(int i=0;i<9;i++)
pd=pd*10+pp[i]-48;
if(!usd[pd]) q.push(nw);
}
}
}
交上去
听取T声一片.jpg
当然最后一个点是起始状态=终点状态
好了我们知道单向\(bfs\)在这里莫得前途了
于是我们可以搞双向\(bfs\)
双向\(bfs\):
int X[9]={1,1,1,2,2,2,3,3,3},Y[9]={1,2,3,1,2,3,1,2,3};//数转矩阵用的
int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
int hy[4][4]=//矩阵的位置对应的数的位置
{{0,0,0,0},
{0,0,1,2},
{0,3,4,5},
{0,6,7,8}
};
string k;
queue<qwq> q1,q2;
map<int,int> usd,bu1,bu2;//usd记录被哪一边访问过,bu1记录q1中状态的步数,bu2记录q2中状态的步数(感觉bu1和bu2可以和起来的样子)
void bfs()
{
while(1)
{
bool bj=0;
if(q1.size()>q2.size())//哪边少先扩展哪边
{
qwq nw=q2.front();
q2.pop();
int lg=nw.lig;
for(int i=0;i<4;i++)
{
int xx=X[lg]+dx[i],yy=Y[lg]+dy[i];//枚举0的转移位置
if(xx<1||xx>3||yy<1||yy>3) continue;
string s1=nw.u;int pd=0;
swap(s1[lg],s1[hy[xx][yy]]);//懒得手写swap了
for(int i=0;i<9;i++)
pd=pd*10+s1[i]-48;
if(usd[pd]==1) {printf("%d",nw.bs+1+bu1[pd]);bj=1;break;}//如果两边搜的点能接上,则找到最短步数
if(!usd[pd]) //如果该状态还未被访问过
{
qwq nxt;nxt.u=s1;nxt.lig=hy[xx][yy];
usd[pd]=2;nxt.bs=nw.bs+1;bu2[pd]=nxt.bs;q2.push(nxt);
}
}
}
else
{
qwq nw=q1.front();
q1.pop();
int lg=nw.lig;
for(int i=0;i<4;i++)
{
int xx=X[lg]+dx[i],yy=Y[lg]+dy[i];
if(xx<1||xx>3||yy<1||yy>3) continue;
string s1=nw.u;int pd=0;
swap(s1[lg],s1[hy[xx][yy]]);
for(int i=0;i<9;i++)
pd=pd*10+s1[i]-48;
if(usd[pd]==2) {printf("%d",nw.bs+1+bu2[pd]);bj=1;break;}
if(!usd[pd])
{
qwq nxt;nxt.u=s1;nxt.lig=hy[xx][yy];
usd[pd]=1;nxt.bs=nw.bs+1;bu1[pd]=nxt.bs;q1.push(nxt);
}
}
}
if(bj) break;//找到答案就跳出
}
}
当然,要注意特判起始状态=终点状态的情况
然后就\(AC\)了,而且跑的很快
啥?你说A*?双向bfs能搞定的东西用什么A*