题意
给出一个九宫格(3*3),上面有1~8共八个数字块按某个顺序排列,而有一个空格,让你通过移动使它排列成12345678空.
普通版V1.0
Aizu - ALDS1_13_B 8 Puzzle
经典版的,不过只需要输出移动次数因此可能有什么高级解法,不过我还是按书的模板打的可输出路径版.
#include<bits/stdc++.h>
using namespace std;
const int dx[4]={0,0,-1,1},dy[4]={-1,1,0,0};
const char rd[4]={'u','d','l','r'};
struct gg{
int mp[9];
int space;
string path;
bool operator<(const gg &tmp)const
{
for(int i=0;i<=8;i++)
{
if(mp[i]!=tmp.mp[i])return mp[i]>tmp.mp[i];
}
return false;
}
};
bool ifok(gg a)
{
for(int i=0;i<8;i++)
if(a.mp[i]!=i+1)return false;
return true;
}
gg bfs(gg bg)
{
queue<gg> q;gg now,tmp;int x,y;
map<gg,bool>vis;
vis[bg]=1;
q.push(bg);
while(!q.empty())
{
now=q.front();q.pop();
if(ifok(now))return now;
x=now.space%3;y=now.space/3;
for(int i=0;i<=3;i++)
{
tmp=now;
if(x+dx[i]<0||x+dx[i]>2||y+dy[i]<0||y+dy[i]>2)continue;
tmp.space=x+dx[i]+(y+dy[i])*3;
swap(tmp.mp[x+y*3],tmp.mp[x+dx[i]+(y+dy[i])*3]);
tmp.path+=rd[i];
if(vis[tmp])continue;
vis[tmp]=1;q.push(tmp);
}
}
}
int main()
{
gg bg,ans;
for(int i=0;i<=8;i++)
{
scanf("%d",&bg.mp[i]);
if(bg.mp[i]==0)bg.space=i;
}
bg.path="";
ans=bfs(bg);
cout<<ans.path.length()<<endl;//话说aoj老是卡换行,没有就pe...
}
升级版V2.0
HDU - 1043 八数码
数据不只一组,因此每次搜会tle加mle…所以首先预处理,从结尾情况往回推出每一种情况的最短路径,然后O(1)查询就好了,注意因为是反推的,所以答案反着输出就行了.
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const short dx[4]={0,0,-1,1},dy[4]={-1,1,0,0};
const char rd[4]={'d','u','r','l'};
struct gg{
short mp[9];
short space;
string path;
bool operator<(const gg &tmp)const
{
for(short i=0;i<=8;i++)
{
if(mp[i]!=tmp.mp[i])return mp[i]>tmp.mp[i];
}
return false;
}
};map<gg,bool>vis;
void bfs(gg bg)
{ queue<gg> q;
gg now,tmp;short x,y;
vis[bg]=1;
q.push(bg);
while(!q.empty())
{
now=q.front();q.pop();
x=now.space%3;y=now.space/3;
for(short i=0;i<=3;i++)
{
tmp=now;
if(x+dx[i]<0||x+dx[i]>2||y+dy[i]<0||y+dy[i]>2)continue;
tmp.space=x+dx[i]+(y+dy[i])*3;
swap(tmp.mp[x+y*3],tmp.mp[x+dx[i]+(y+dy[i])*3]);
tmp.path+=rd[i];
if(vis[tmp])continue;
vis[tmp]=1;q.push(tmp);
}
}
}
int main()
{
gg bg,ans;char c;string s;
for(int i=0;i<8;i++)
bg.mp[i]=i+1;
bg.mp[8]=0;bg.space=8;bg.path="";bfs(bg);
while(~scanf(" %c",&c))
{if(c=='x')bg.space=0,bg.mp[0]=0;
else bg.mp[0]=c-'0';
for(short i=1;i<=8;i++)
{scanf(" %c",&c);
if(c=='x')bg.space=i,bg.mp[i]=0;
else bg.mp[i]=c-'0';}
bg.path="";
map<gg,bool>::iterator pos=vis.find(bg);
if(pos!=vis.end())
{
s=pos->first.path;
reverse(s.begin(),s.end());
}
else s="unsolvable";
cout<<s<<endl;
}
}
终极版VMax.0
POJ - 1077 Eight
题面与之前相同,只有一组测试输出路径,不过貌似数据特别特别强大,之前1.0版和2.0版都过不了(tle)…没办法只能写了1.0的astar版(话说改改估计连15数码都可以水过了哈)
#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<map>
using namespace std;
const int dx[4]={0,0,-1,1},dy[4]={-1,1,0,0};
const char rd[4]={'u','d','l','r'};
int mdd[9][9];
struct gg{
int mp[9],md;
int space;
string path;
bool operator<(const gg &tmp)const
{
for(int i=0;i<=8;i++)
{
if(mp[i]!=tmp.mp[i])return mp[i]>tmp.mp[i];
}
return false;
}
};
struct state{
gg g;
int e;
friend bool operator<(state a,state b)
{
return a.e>b.e;
}
};
int getmd(gg g)
{
int ret=0;
for(int i=0;i<=8;i++)
if(g.mp[i]!=0)ret+=mdd[i][g.mp[i]-1];
return ret;
}
gg bfs(gg bg)
{
priority_queue<state> q;gg now,tmp;int x,y;state tp,ne;
map<gg,bool>vis;
state init;
init.g=bg;init.e=getmd(bg);
q.push(init);
while(!q.empty())
{
tp=q.top();q.pop();now=tp.g;
if(now.md==0)return now;
vis[now]=1;
x=now.space%3;y=now.space/3;
for(int i=0;i<=3;i++)
{
if(x+dx[i]<0||x+dx[i]>2||y+dy[i]<0||y+dy[i]>2)continue;
tmp=now;tmp.md-=mdd[x+dx[i]+(y+dy[i])*3][tmp.mp[x+dx[i]+(y+dy[i])*3]-1];
tmp.md+=mdd[now.space][tmp.mp[x+dx[i]+(y+dy[i])*3]-1];
tmp.space=x+dx[i]+(y+dy[i])*3;
swap(tmp.mp[x+y*3],tmp.mp[x+dx[i]+(y+dy[i])*3]);
tmp.path+=rd[i];
if(vis[tmp])continue;
ne.g=tmp;ne.e=tmp.path.length()+tmp.md;q.push(ne);
}
}
printf("unsolvable");
}
int main()
{ //freopen("in.txt","r",stdin);
for(int i=0;i<=8;i++)
for(int j=0;j<=8;j++)
mdd[i][j]=abs(i/3-j/3)+abs(i%3-j%3);
gg bg,ans;char c;
for(int i=0;i<=8;i++)
{
scanf(" %c",&c);
if(c=='x')bg.space=i,bg.mp[i]=0;
else bg.mp[i]=c-'0';
}
bg.path="";bg.md=getmd(bg);
ans=bfs(bg);
cout<<ans.path;
}
小总结:astar是bfs加强版,每次选取操作的元素是按一个函数f来搞的,而f是由一个当前操作数和一个估计还要操作数组成(估价函数h),这就使如果估价函数选的好可以让程序跑得飞起来,例如此处选的估价函数就是每个数到它应在位置的曼哈顿距离之和.