菜鸟就要老老实实重新学起:
八数码:
很经典的搜索问题,写这个练练搜索。
八数码问题就是给出一个3*3的矩阵其中有1~8数字和一个空格‘x’,可以移动周围的数字到这个空格上,要求某种排列恢复到{{1,2,3},{4,5,6},{7,8,x}}形式的移动方法。
由大神的八数码八境界想自己实现一下 http://www.cnblogs.com/goodness/archive/2010/05/04/1727141.html
一、暴力BFS+STL
就是直接暴力bfs所有的询问,string储存当前状态,vector<int>储存当前路径,map判重,好写,但时间感人。
代码:
#define N 512345
char s[101];
struct node
{
string str;
vector<int>res;
int index;
};
map<string,int> mp;
vector<int>res;
bool bfs(string ss)
{
char c;
queue<node> q;
while(!q.empty())q.pop();
node g,h;
g.str="12345678x";g.res.clear();g.index=8;
q.push(g);
while(!q.empty())
{
g=q.front();q.pop();
if(g.str==ss)
{
res=g.res;
return true;
}
h=g;
if((h.index+1)%3!=0)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index+1];h.str[h.index+1]=c;
h.index++;
h.res.push_back(4);
if(mp[h.str]==0)
{
mp[h.str]=true;
q.push(h);
}
}
h=g;
if(h.index%3!=0)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index-1];h.str[h.index-1]=c;
h.index--;
h.res.push_back(3);
if(!mp[h.str])
{
mp[h.str]=true;
q.push(h);
}
}
h=g;
if(h.index<6)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index+3];h.str[h.index+3]=c;
h.index+=3;
h.res.push_back(2);
if(!mp[h.str])
{
mp[h.str]=true;
q.push(h);
}
}
h=g;
if(h.index>2)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index-3];h.str[h.index-3]=c;
h.index-=3;
h.res.push_back(1);
if(!mp[h.str])
{
mp[h.str]=true;
q.push(h);
}
}
}
return false;
}
int main()
{
int i,j,k,kk,t,x,y,z;
while(scanf("%s",s)!=EOF)
{
mp.clear();
for(i=1;i<9;i++)
scanf("%s",s+i);
string ss(s);
if(bfs(ss))
for(i=res.size()-1;i>=0;i--)
{
if(res[i]==1)printf("d");
if(res[i]==2)printf("u");
if(res[i]==3)printf("r");
if(res[i]==4)printf("l");
}
else
printf("unsolvable");
printf("\n");
}
return 0;
}
二、暴力BFS+hash判重
与上一方法基本相同,就是减少stl的使用,改用char[]存状态,用康托展开计算全排列数值从而得到哈希值判重。
能够有效缩减时间,但是询问多时尤其是在无解时耗时依然较多。
关于康托展开:http://blog.youkuaiyun.com/kopyh/article/details/48377291
代码:
#define N 512345
char ss[10];
struct node
{
char str[10];
vector<int>res;
int index,hashnum;
};
vector<int>res;
bool vis[N];
int fac[] = {1,1,2,6,24,120,720,5040,40320};
int over;
int KT(char ss[])
{
int i, j, t, sum;
int s[10];
for(i=0;i<9;i++)
{
if(ss[i]=='x')
s[i]=0;
else
s[i]=ss[i]-'0';
}
sum = 0;
for (i=0; i<9; i++)
{
t = 0;
for (j=i+1; j<9; j++)
if (s[j] < s[i])
t++;
sum += t*fac[9-i-1];
}
return sum+1;
}
bool bfs(char ss[])
{
char c;
queue<node> q;
while(!q.empty())q.pop();
over = KT(ss);
node g,h;
for(int i=0;i<8;i++)
g.str[i]=i+1+'0';
g.str[8]='x';g.str[9]='\0';
g.res.clear();g.index=8;
q.push(g);
while(!q.empty())
{
g=q.front();q.pop();
if(KT(g.str)==over)
{
res=g.res;
return true;
}
h=g;
if((h.index+1)%3!=0)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index+1];h.str[h.index+1]=c;
h.index++;
h.res.push_back(4);
int t=KT(h.str);
if(!vis[t])
{
vis[t]=true;
q.push(h);
}
}
h=g;
if(h.index%3!=0)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index-1];h.str[h.index-1]=c;
h.index--;
h.res.push_back(3);
int t=KT(h.str);
if(!vis[t])
{
vis[t]=true;
q.push(h);
}
}
h=g;
if(h.index<6)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index+3];h.str[h.index+3]=c;
h.index+=3;
h.res.push_back(2);
int t=KT(h.str);
if(!vis[t])
{
vis[t]=true;
q.push(h);
}
}
h=g;
if(h.index>2)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index-3];h.str[h.index-3]=c;
h.index-=3;
h.res.push_back(1);
int t=KT(h.str);
if(!vis[t])
{
vis[t]=true;
q.push(h);
}
}
}
return false;
}
int main()
{
int i,j,k,kk,t,x,y,z;
while(scanf("%s",ss)!=EOF)
{
memset(vis,false,sizeof(vis));
for(i=1;i<9;i++)
scanf("%s",ss+i);
if(bfs(ss))
for(i=res.size()-1;i>=0;i--)
{
if(res[i]==1)printf("d");
if(res[i]==2)printf("u");
if(res[i]==3)printf("r");
if(res[i]==4)printf("l");
}
else
printf("unsolvable");
printf("\n");
}
return 0;
}
三、暴力BFS+hash判重 +打表
与上一方法基本相同,就是改成一次BFS所有可能进行打表,时间可以保持在200ms内解决问题。
代码:
#define N 512345
char ss[10];
struct node
{
char str[10];
vector<int>res;
int index,num;
};
vector<int>res[N];
bool vis[N];
int fac[] = {1,1,2,6,24,120,720,5040,40320};
int over;
int ans[N];
int KT(char ss[])
{
int i, j, t, sum;
int s[10];
for(i=0;i<9;i++)
{
if(ss[i]=='x')
s[i]=0;
else
s[i]=ss[i]-'0';
}
sum = 0;
for (i=0; i<9; i++)
{
t = 0;
for (j=i+1; j<9; j++)
if (s[j] < s[i])
t++;
sum += t*fac[9-i-1];
}
return sum+1;
}
bool bfs()
{
char c;
queue<node> q;
while(!q.empty())q.pop();
over = KT(ss);
node g,h;
for(int i=0;i<8;i++)
g.str[i]=i+1+'0';
g.str[8]='x';g.str[9]='\0';
g.res.clear();g.index=8;g.num=0;
int t=KT(g.str);
vis[t] = true;
q.push(g);
while(!q.empty())
{
g=q.front();q.pop();
int t = KT(g.str);
res[t] = g.res;
g.num++;
h=g;
if((h.index+1)%3!=0)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index+1];h.str[h.index+1]=c;
h.index++;
h.res.push_back(4);
int t=KT(h.str);
if(!vis[t])
{
vis[t]=true;
q.push(h);
}
}
h=g;
if(h.index%3!=0)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index-1];h.str[h.index-1]=c;
h.index--;
h.res.push_back(3);
int t=KT(h.str);
if(!vis[t])
{
vis[t]=true;
q.push(h);
}
}
h=g;
if(h.index<6)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index+3];h.str[h.index+3]=c;
h.index+=3;
h.res.push_back(2);
int t=KT(h.str);
if(!vis[t])
{
vis[t]=true;
q.push(h);
}
}
h=g;
if(h.index>2)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index-3];h.str[h.index-3]=c;
h.index-=3;
h.res.push_back(1);
int t=KT(h.str);
if(!vis[t])
{
vis[t]=true;
q.push(h);
}
}
}
return false;
}
int main()
{
int i,j,k,kk,t,x,y,z;
for(i=0;i<N;i++)
res[i].clear();
bfs();
while(scanf("%s",ss)!=EOF)
{
memset(vis,false,sizeof(vis));
for(i=1;i<9;i++)
scanf("%s",ss+i);
t = KT(ss);
if(res[t].size()!=0||t==46234)
for(i=res[t].size()-1;i>=0;i--)
{
if(res[t][i]==1)printf("d");
if(res[t][i]==2)printf("u");
if(res[t][i]==3)printf("r");
if(res[t][i]==4)printf("l");
}
else
printf("unsolvable");
printf("\n");
}
return 0;
}
四、暴力BFS+hash判重 +打表+回溯记录路径
这样写已经很简单了,时间也很快,但是直接把所有路径存在vector中会爆内存,所以需要改用路径回溯记录路径。
这样已经可以解决poj1077和hdu1043的八数码问题了。
代码:
#define N 512345
char ss[10];
struct node
{
char str[10];
int pos;
int index;
};
struct res
{
int now,fa;
}res[N];
int vis[N];
int fac[] = {1,1,2,6,24,120,720,5040,40320};
int over,cnt;
int ans[N];
int KT(char ss[])
{
int i, j, t, sum;
int s[10];
for(i=0;i<9;i++)
{
if(ss[i]=='x')
s[i]=0;
else
s[i]=ss[i]-'0';
}
sum = 0;
for (i=0; i<9; i++)
{
t = 0;
for (j=i+1; j<9; j++)
if (s[j] < s[i])
t++;
sum += t*fac[9-i-1];
}
return sum+1;
}
bool bfs()
{
char c;
queue<node> q;
while(!q.empty())q.pop();
over = KT(ss);
node g,h;
for(int i=0;i<8;i++)
g.str[i]=i+1+'0';
g.str[8]='x';g.str[9]='\0';
g.pos=0;g.index=8;
int t=KT(g.str);
vis[t] = 0;
q.push(g);
cnt=1;
while(!q.empty())
{
g=q.front();q.pop();
int t = KT(g.str);
h=g;
if((h.index+1)%3!=0)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index+1];h.str[h.index+1]=c;
h.index++;
res[cnt].now=4;res[cnt].fa=h.pos;h.pos=cnt;cnt++;
int t=KT(h.str);
if(vis[t]==-1)
{
vis[t]=cnt-1;
q.push(h);
}
}
h=g;
if(h.index%3!=0)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index-1];h.str[h.index-1]=c;
h.index--;
res[cnt].now=3;res[cnt].fa=h.pos;h.pos=cnt;cnt++;
int t=KT(h.str);
if(vis[t]==-1)
{
vis[t]=cnt-1;
q.push(h);
}
}
h=g;
if(h.index<6)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index+3];h.str[h.index+3]=c;
h.index+=3;
res[cnt].now=2;res[cnt].fa=h.pos;h.pos=cnt;cnt++;
int t=KT(h.str);
if(vis[t]==-1)
{
vis[t]=cnt-1;
q.push(h);
}
}
h=g;
if(h.index>2)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index-3];h.str[h.index-3]=c;
h.index-=3;
res[cnt].now=1;res[cnt].fa=h.pos;h.pos=cnt;cnt++;
int t=KT(h.str);
if(vis[t]==-1)
{
vis[t]=cnt-1;
q.push(h);
}
}
}
return false;
}
int main()
{
int i,j,k,kk,t,x,y,z;
memset(vis,-1,sizeof(vis));
res[0].now=-1;
bfs();
while(scanf("%s",ss)!=EOF)
{
for(i=1;i<9;i++)
scanf("%s",ss+i);
t = KT(ss);
if(vis[t]!=-1)
{
t = vis[t];
while(res[t].now!=-1)
{
if(res[t].now==1)printf("d");
if(res[t].now==2)printf("u");
if(res[t].now==3)printf("r");
if(res[t].now==4)printf("l");
t=res[t].fa;
}
}
else
printf("unsolvable");
printf("\n");
}
return 0;
}
五、双向广搜+hash
就是将bfs改成双广,依旧康托展开计算hash值判重,同时回溯记录路径,但是双广在无解时依旧要扫到所有情况,所以只是理论上时空缩小一半,极端条件依旧感人。
代码:
#define N 512345
char ss[10];
struct node
{
char str[10];
int mark,num;
int index,hashnum;
};
struct res
{
int fa,now;
}res1[N],res2[N];
int vis1[N],vis2[N];
int fac[] = {1,1,2,6,24,120,720,5040,40320};
int over,ans1,ans2,sum1,sum2,cnt1,cnt2;
int hehe[N];
int KT(char ss[])
{
int i, j, t, sum;
int s[10];
for(i=0;i<9;i++)
{
if(ss[i]=='x')
s[i]=0;
else
s[i]=ss[i]-'0';
}
sum = 0;
for (i=0; i<9; i++)
{
t = 0;
for (j=i+1; j<9; j++)
if (s[j] < s[i])
t++;
sum += t*fac[9-i-1];
}
return sum+1;
}
bool bfs(char ss[], int temp)
{
char c;
queue<node> q;
while(!q.empty())q.pop();
node g,h;
for(int i=0;i<8;i++)
g.str[i]=i+1+'0';
g.str[8]='x';g.str[9]='\0';
g.mark=0;g.num=0;g.index=8;
vis1[KT(g.str)]=0;
q.push(g);
for(int i=0;i<9;i++)
g.str[i]=ss[i];
g.mark=1;g.num=0;g.index=temp;
vis2[KT(g.str)]=0;
q.push(g);
sum1=sum2=1;
cnt1=cnt2=1;
while(!q.empty())
{
g=q.front();q.pop();
int t = KT(g.str);
if(g.mark==0)sum1--;
else sum2--;
if(g.mark==0&&vis2[t]!=-1)
{
ans1=g.num;
ans2=vis2[t];
return true;
}
if(g.mark==1&&vis1[t]!=-1)
{
ans2=g.num;
ans1=vis1[t];
return true;
}
h=g;
if((h.index+1)%3!=0)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index+1];h.str[h.index+1]=c;
h.index++;
if(!h.mark){res1[cnt1].now=4;res1[cnt1].fa=h.num;h.num=cnt1;cnt1++;}
else {res2[cnt2].now=4;res2[cnt2].fa=h.num;h.num=cnt2;cnt2++;}
int t=KT(h.str);
if(vis1[t]==-1&&!h.mark)
{
vis1[t]=h.num;
q.push(h);
sum1++;
}
else if(vis2[t]==-1&&h.mark)
{
vis2[t]=h.num;
q.push(h);
sum2++;
}
}
h=g;
if(h.index%3!=0)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index-1];h.str[h.index-1]=c;
h.index--;
if(!h.mark){res1[cnt1].now=3;res1[cnt1].fa=h.num;h.num=cnt1;cnt1++;}
else {res2[cnt2].now=3;res2[cnt2].fa=h.num;h.num=cnt2;cnt2++;}
int t=KT(h.str);
if(vis1[t]==-1&&!h.mark)
{
vis1[t]=h.num;
q.push(h);
sum1++;
}
else if(vis2[t]==-1&&h.mark)
{
vis2[t]=h.num;
q.push(h);
sum2++;
}
}
h=g;
if(h.index<6)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index+3];h.str[h.index+3]=c;
h.index+=3;
if(!h.mark){res1[cnt1].now=2;res1[cnt1].fa=h.num;h.num=cnt1;cnt1++;}
else {res2[cnt2].now=2;res2[cnt2].fa=h.num;h.num=cnt2;cnt2++;}
int t=KT(h.str);
if(vis1[t]==-1&&!h.mark)
{
vis1[t]=h.num;
q.push(h);
sum1++;
}
else if(vis2[t]==-1&&h.mark)
{
vis2[t]=h.num;
q.push(h);
sum2++;
}
}
h=g;
if(h.index>2)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index-3];h.str[h.index-3]=c;
h.index-=3;
if(!h.mark){res1[cnt1].now=1;res1[cnt1].fa=h.num;h.num=cnt1;cnt1++;}
else {res2[cnt2].now=1;res2[cnt2].fa=h.num;h.num=cnt2;cnt2++;}
int t=KT(h.str);
if(vis1[t]==-1&&!h.mark)
{
vis1[t]=h.num;
q.push(h);
sum1++;
}
else if(vis2[t]==-1&&h.mark)
{
vis2[t]=h.num;
q.push(h);
sum2++;
}
}
if(sum1==0||sum2==0)
return false;
}
return false;
}
int main()
{
int i,j,k,kk,t,x,y,z;
while(scanf("%s",ss)!=EOF)
{
if(ss[0]=='x')x=0;
for(i=1;i<9;i++)
{
scanf("%s",ss+i);
if(ss[i]=='x') x=i;
}
ans1=ans2=-1;
memset(vis1,-1,sizeof(vis1));
memset(vis2,-1,sizeof(vis2));
res1[0].now=res2[0].now=-1;
if(bfs(ss,x))
{
x=0;
if(res2[ans2].now==-1&&res1[ans1].now==-1)
{
printf("lr\n");
continue;
}
while(res2[ans2].now!=-1)
{
hehe[x++]=res2[ans2].now;
ans2=res2[ans2].fa;
}
for(i=x-1;i>=0;i--)
{
if(hehe[i]==1)printf("u");
if(hehe[i]==2)printf("d");
if(hehe[i]==3)printf("l");
if(hehe[i]==4)printf("r");
}
while(res1[ans1].now!=-1)
{
if(res1[ans1].now==1)printf("d");
if(res1[ans1].now==2)printf("u");
if(res1[ans1].now==3)printf("r");
if(res1[ans1].now==4)printf("l");
ans1=res1[ans1].fa;
}
}
else
printf("unsolvable");
printf("\n");
}
return 0;
}
六、双向广搜+hash+逆序数判无解
双向广搜就是从起点终点同时开始搜索,当初碰到时就是有解,所以理论上可以缩减一半的时空代价。
鉴于双广在无解时表现很差,所以加上逆序数判无解状态,就可以轻松过hdu和poj的八数码了。
所谓逆序数判无解态是因为两个能够互相移动成的状态的逆序数的奇偶性是相同的。
代码:
#define N 512345
char ss[10];
struct node
{
char str[10];
int mark,num;
int index,hashnum;
};
struct res
{
int fa,now;
}res1[N],res2[N];
int vis1[N],vis2[N];
int fac[] = {1,1,2,6,24,120,720,5040,40320};
int over,ans1,ans2,sum1,sum2,cnt1,cnt2;
int hehe[N];
int KT(char ss[])
{
int i, j, t, sum;
int s[10];
for(i=0;i<9;i++)
{
if(ss[i]=='x')
s[i]=0;
else
s[i]=ss[i]-'0';
}
sum = 0;
for (i=0; i<9; i++)
{
t = 0;
for (j=i+1; j<9; j++)
if (s[j] < s[i])
t++;
sum += t*fac[9-i-1];
}
return sum+1;
}
bool bfs(char ss[], int temp)
{
char c;
queue<node> q;
while(!q.empty())q.pop();
node g,h;
for(int i=0;i<8;i++)
g.str[i]=i+1+'0';
g.str[8]='x';g.str[9]='\0';
g.mark=0;g.num=0;g.index=8;
vis1[KT(g.str)]=0;
q.push(g);
for(int i=0;i<9;i++)
g.str[i]=ss[i];
g.mark=1;g.num=0;g.index=temp;
vis2[KT(g.str)]=0;
q.push(g);
sum1=sum2=1;
cnt1=cnt2=1;
while(!q.empty())
{
g=q.front();q.pop();
int t = KT(g.str);
if(g.mark==0)sum1--;
else sum2--;
if(g.mark==0&&vis2[t]!=-1)
{
ans1=g.num;
ans2=vis2[t];
return true;
}
if(g.mark==1&&vis1[t]!=-1)
{
ans2=g.num;
ans1=vis1[t];
return true;
}
h=g;
if((h.index+1)%3!=0)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index+1];h.str[h.index+1]=c;
h.index++;
if(!h.mark){res1[cnt1].now=4;res1[cnt1].fa=h.num;h.num=cnt1;cnt1++;}
else {res2[cnt2].now=4;res2[cnt2].fa=h.num;h.num=cnt2;cnt2++;}
int t=KT(h.str);
if(vis1[t]==-1&&!h.mark)
{
vis1[t]=h.num;
q.push(h);
sum1++;
}
else if(vis2[t]==-1&&h.mark)
{
vis2[t]=h.num;
q.push(h);
sum2++;
}
}
h=g;
if(h.index%3!=0)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index-1];h.str[h.index-1]=c;
h.index--;
if(!h.mark){res1[cnt1].now=3;res1[cnt1].fa=h.num;h.num=cnt1;cnt1++;}
else {res2[cnt2].now=3;res2[cnt2].fa=h.num;h.num=cnt2;cnt2++;}
int t=KT(h.str);
if(vis1[t]==-1&&!h.mark)
{
vis1[t]=h.num;
q.push(h);
sum1++;
}
else if(vis2[t]==-1&&h.mark)
{
vis2[t]=h.num;
q.push(h);
sum2++;
}
}
h=g;
if(h.index<6)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index+3];h.str[h.index+3]=c;
h.index+=3;
if(!h.mark){res1[cnt1].now=2;res1[cnt1].fa=h.num;h.num=cnt1;cnt1++;}
else {res2[cnt2].now=2;res2[cnt2].fa=h.num;h.num=cnt2;cnt2++;}
int t=KT(h.str);
if(vis1[t]==-1&&!h.mark)
{
vis1[t]=h.num;
q.push(h);
sum1++;
}
else if(vis2[t]==-1&&h.mark)
{
vis2[t]=h.num;
q.push(h);
sum2++;
}
}
h=g;
if(h.index>2)
{
c=h.str[h.index];h.str[h.index]=h.str[h.index-3];h.str[h.index-3]=c;
h.index-=3;
if(!h.mark){res1[cnt1].now=1;res1[cnt1].fa=h.num;h.num=cnt1;cnt1++;}
else {res2[cnt2].now=1;res2[cnt2].fa=h.num;h.num=cnt2;cnt2++;}
int t=KT(h.str);
if(vis1[t]==-1&&!h.mark)
{
vis1[t]=h.num;
q.push(h);
sum1++;
}
else if(vis2[t]==-1&&h.mark)
{
vis2[t]=h.num;
q.push(h);
sum2++;
}
}
if(sum1==0||sum2==0)
return false;
}
return false;
}
bool inverse(char ss[])
{
int t=0,x,y;
for(int i=1;i<9;i++)
for(int j=0;j<i;j++)
{
if(ss[j]=='x')continue;
else x=ss[j]-'0';
if(ss[i]=='x')continue;
else y=ss[i]-'0';
if(x>y)
t++;
}
if(t&1)
return true;
return false;
}
int main()
{
int i,j,k,kk,t,x,y,z;
while(scanf("%s",ss)!=EOF)
{
if(ss[0]=='x')x=0;
for(i=1;i<9;i++)
{
scanf("%s",ss+i);
if(ss[i]=='x') x=i;
}
if(inverse(ss))
{
printf("unsolvable\n");
continue;
}
ans1=ans2=-1;
memset(vis1,-1,sizeof(vis1));
memset(vis2,-1,sizeof(vis2));
res1[0].now=res2[0].now=-1;
if(bfs(ss,x))
{
x=0;
if(res2[ans2].now==-1&&res1[ans1].now==-1)
{
printf("lr\n");
continue;
}
while(res2[ans2].now!=-1)
{
hehe[x++]=res2[ans2].now;
ans2=res2[ans2].fa;
}
for(i=x-1;i>=0;i--)
{
if(hehe[i]==1)printf("u");
if(hehe[i]==2)printf("d");
if(hehe[i]==3)printf("l");
if(hehe[i]==4)printf("r");
}
while(res1[ans1].now!=-1)
{
if(res1[ans1].now==1)printf("d");
if(res1[ans1].now==2)printf("u");
if(res1[ans1].now==3)printf("r");
if(res1[ans1].now==4)printf("l");
ans1=res1[ans1].fa;
}
}
else
printf("unsolvable");
printf("\n");
}
return 0;
}
七、A*+康托展开hash+逆序数+曼哈顿
A*搜索是一种智能化搜索,就是在BFS基础上增加估价函数,以估价函数排序进行搜索,也就是优先队列优化,这里用的曼哈顿距离就是空间坐标距离。
直接写A*,同时用hash判重,逆序数排出无解。
A星搜索的优先函数用曼哈顿距离,到终点距离为第一参考到起点距离为第二参考。
代码:
#define N 512345
char s[10];
struct node
{
int r,w,hashnum,pos,num;
char str[10];
friend bool operator < (node a, node b)
{
if(a.r==b.r)return a.w > b.w;
return a.r > b.r;
}
}tn;
struct res
{
int now,fa;
}res[N];
int fac[] = {1,1,2,6,24,120,720,5040,40320};
int vis[N],hehe[N];
char init[10]="12345678x";
bool inverse(char ss[])
{
int t=0,x,y;
for(int i=1;i<9;i++)
for(int j=0;j<i;j++)
{
if(ss[j]=='x')continue;
else x=ss[j]-'0';
if(ss[i]=='x')continue;
else y=ss[i]-'0';
if(x>y)
t++;
}
if(t&1)
return true;
return false;
}
int KT(char ss[])
{
int i, j, t, sum;
int s[10];
for(i=0;i<9;i++)
{
if(ss[i]=='x')
s[i]=0;
else
s[i]=ss[i]-'0';
}
sum = 0;
for (i=0; i<9; i++)
{
t = 0;
for (j=i+1; j<9; j++)
if (s[j] < s[i])
t++;
sum += t*fac[9-i-1];
}
return sum+1;
}
int len(node tmp)
{
int ans=0;
for(int i=0;i<9;i++)
{
if(tmp.str[i]!='x')
ans+=abs(i/3-(tmp.str[i]-'0'-1)/3)+abs(i%3-((tmp.str[i]-'0')-1)%3);
else
ans+=(2-i/3)+(2-i%3);
}
return ans;
}
int Astar(int now)
{
char c;
priority_queue<node> q;
while(!q.empty())q.pop();
node g,h;
int over = KT(init);
for(int i=0;i<10;i++)
g.str[i]=s[i];
g.hashnum=KT(g.str);g.pos=now;g.num=0;
g.r=len(g);g.w=0;
vis[g.hashnum]=g.num;
q.push(g);
int cnt=1;
while(!q.empty())
{
g=q.top();q.pop();
if(g.hashnum==over)
return g.num;
h=g;
if((h.pos+1)%3!=0)
{
c=h.str[h.pos];h.str[h.pos]=h.str[h.pos+1];h.str[h.pos+1]=c;
h.pos++;
h.w++;h.r=len(h);h.hashnum=KT(h.str);
res[cnt].now=4;res[cnt].fa=h.num;h.num=cnt;cnt++;
if(vis[h.hashnum]==-1)
{
vis[h.hashnum]=cnt-1;
q.push(h);
}
}
h=g;
if(h.pos%3!=0)
{
c=h.str[h.pos];h.str[h.pos]=h.str[h.pos-1];h.str[h.pos-1]=c;
h.pos--;
h.w++;h.r=len(h);h.hashnum=KT(h.str);
res[cnt].now=3;res[cnt].fa=h.num;h.num=cnt;cnt++;
if(vis[h.hashnum]==-1)
{
vis[h.hashnum]=cnt-1;
q.push(h);
}
}
h=g;
if(h.pos<6)
{
c=h.str[h.pos];h.str[h.pos]=h.str[h.pos+3];h.str[h.pos+3]=c;
h.pos+=3;
h.w++;h.r=len(h);h.hashnum=KT(h.str);
res[cnt].now=2;res[cnt].fa=h.num;h.num=cnt;cnt++;
if(vis[h.hashnum]==-1)
{
vis[h.hashnum]=cnt-1;
q.push(h);
}
}
h=g;
if(h.pos>2)
{
c=h.str[h.pos];h.str[h.pos]=h.str[h.pos-3];h.str[h.pos-3]=c;
h.pos-=3;
h.w++;h.r=len(h);h.hashnum=KT(h.str);
res[cnt].now=1;res[cnt].fa=h.num;h.num=cnt;cnt++;
if(vis[h.hashnum]==-1)
{
vis[h.hashnum]=cnt-1;
q.push(h);
}
}
}
return -1;
}
int main()
{
int i,j,k,kk,t,x,y,z;
while(scanf("%s",s)!=EOF)
{
if(s[0]=='x')x=0;
for(i=1;i<9;i++)
{
scanf("%s",s+i);
if(s[i]=='x')x=i;
}
if(inverse(s))
{
printf("unsolvable\n");
continue;
}
res[0].now=-1;
memset(vis,-1,sizeof(vis));
t = Astar(x);
x=0;
while(res[t].now!=-1)
hehe[x++]=res[t].now,t=res[t].fa;
for(i=x-1;i>=0;i--)
{
if(hehe[i]==1)printf("u");
if(hehe[i]==2)printf("d");
if(hehe[i]==3)printf("l");
if(hehe[i]==4)printf("r");
}
printf("\n");
}
return 0;
}
八、IDA*+逆序数+曼哈顿
IDA*搜索就是迭代深化A*搜索,其实是深搜,通过估价函数求出最大搜索深度,不断深搜当超过这个最大深度时就剪枝,如果未找到解就加大搜索深度上限,可以用刚才所有被剪枝的搜索深度中的最小值,另外IDA*是只向一个方向的深搜,不会回头,所以不需要标记访问,有很好的空间利用,但是同样因为不标记又要迭代多次所以会有很多重复搜索,在时间复杂度上不好估计,总的来说IDA*比较神奇,用最大搜索深度处理深搜,在一定程度上既不像广搜消耗大量空间也不像深搜盲目。在一些大数据量的搜索时有奇效。
在八数码中它的时间不如双向广搜和A*但是消耗空间很小,空间优化很好。
代码:
#define N 1123
char s[10];
int res[N],sum,ans;
bool inverse(char ss[])
{
int t=0,x,y;
for(int i=1;i<9;i++)
for(int j=0;j<i;j++)
{
if(ss[j]=='x')continue;
else x=ss[j]-'0';
if(ss[i]=='x')continue;
else y=ss[i]-'0';
if(x>y)
t++;
}
if(t&1)
return true;
return false;
}
int len(char str[])
{
int ans=0;
for(int i=0;i<9;i++)
{
if(str[i]!='x')
ans+=abs(i/3-(str[i]-'0'-1)/3)+abs(i%3-((str[i]-'0')-1)%3);
else
ans+=(2-i/3)+(2-i%3);
}
return ans;
}
int dfs(int now,int num,int pos)
{
char c;
int val = len(s);
if(val+num>sum)
return val+num;
res[num]=pos;
if(val==0)
{
ans=1;
return num;
}
int tmp1=INF,tmp2;
if(pos!=3&&(now+1)%3!=0)
{
c=s[now];s[now]=s[now+1];s[now+1]=c;
tmp2=dfs(now+1,num+1,4);
if(ans)return tmp2;
tmp1=min(tmp1,tmp2);
c=s[now];s[now]=s[now+1];s[now+1]=c;
}
if(pos!=4&&now%3!=0)
{
c=s[now];s[now]=s[now-1];s[now-1]=c;
tmp2=dfs(now-1,num+1,3);
if(ans)return tmp2;
tmp1=min(tmp1,tmp2);
c=s[now];s[now]=s[now-1];s[now-1]=c;
}
if(pos!=1&&now<6)
{
c=s[now];s[now]=s[now+3];s[now+3]=c;
tmp2=dfs(now+3,num+1,2);
if(ans)return tmp2;
tmp1=min(tmp1,tmp2);
c=s[now];s[now]=s[now+3];s[now+3]=c;
}
if(pos!=2&&now>2)
{
c=s[now];s[now]=s[now-3];s[now-3]=c;
tmp2=dfs(now-3,num+1,1);
if(ans)return tmp2;
tmp1=min(tmp1,tmp2);
c=s[now];s[now]=s[now-3];s[now-3]=c;
}
return tmp1;
}
void IDAStar(int now)
{
sum=len(s);ans=0;
while(!ans)
sum=dfs(now,0,0);
}
int main()
{
int i,j,k,kk,t,x,y,z;
while(scanf("%s",s)!=EOF)
{
if(s[0]=='x')x=0;
for(i=1;i<9;i++)
{
scanf("%s",s+i);
if(s[i]=='x')x=i;
}
if(inverse(s))
{
printf("unsolvable\n");
continue;
}
IDAStar(x);
for(i=1;i<=sum;i++)
{
if(res[i]==1)printf("u");
if(res[i]==2)printf("d");
if(res[i]==3)printf("l");
if(res[i]==4)printf("r");
}
printf("\n");
}
return 0;
}

本文详细介绍了八数码问题的八种解决方案,包括暴力BFS、双向广搜和A*搜索等,每种方法都结合了hash判断、打表、回溯记录路径等策略,以达到提高效率和解决实际问题的目的。通过对经典八数码问题的深入探讨,旨在帮助初学者掌握不同的搜索算法及其优化技巧。
2565

被折叠的 条评论
为什么被折叠?



