八数码第七境界——A*之曼哈顿+康托展开判重+回溯记录路径+逆序数判无解
2017-12-14 18:56
309 查看
(请先看前面6大境界)
A*其实就是以bfs为基础的,就是给了bfs一些条件,让它以优先队列的形式进行。
想快速掌握A*可以看http://blog.csdn.net/qq_36523667/article/details/78786160
A*有两种距离计算公式:
一种是曼哈顿距离:横向距离+纵向距离
一种是欧氏距离:两点间的直线距离
我们这里使用的是曼哈顿距离。
我们希望的路径假设是
4 8 7
1 2 x
5 6 3
我们的结束路径是
1 2 3
4 5 6
7 8 x
我们看到希望的路径和假设的路径1,相差1个位置,
2,相差1个位置,3相差2个位置。。。
就把所有的相差的位置的横向距离和纵向距离加起来求和,就粗略得出了我们至少应该移动的距离。之后我们在优先队列里,这个距离就是我们判断的依据。
上面这个求距离思想的代码表示
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;
}
再给优先队列设置一下判断的依据(就是重写的这个<号)
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;
就行了
全部代码
#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
;
int fac[] = {1,1,2,6,24,120,720,5040,40320};
int vis
,hehe
;
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;
}
补充一点。
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;
这里的重载<号的判断我们需要深究一下。
return a.r > b.r;是代表我们的优先队列优先把剩下的曼哈顿距离最短的放在队列最前面。
那么上面那个if(a.r==b.r)return a.w > b.w; 呢?就是当曼哈顿距离相等的时候,我们就看我们已经走出去的步数。如果走出去的步数越多,我们更倾向于把他放在队列的最前面。
A*其实就是以bfs为基础的,就是给了bfs一些条件,让它以优先队列的形式进行。
想快速掌握A*可以看http://blog.csdn.net/qq_36523667/article/details/78786160
A*有两种距离计算公式:
一种是曼哈顿距离:横向距离+纵向距离
一种是欧氏距离:两点间的直线距离
我们这里使用的是曼哈顿距离。
我们希望的路径假设是
4 8 7
1 2 x
5 6 3
我们的结束路径是
1 2 3
4 5 6
7 8 x
我们看到希望的路径和假设的路径1,相差1个位置,
2,相差1个位置,3相差2个位置。。。
就把所有的相差的位置的横向距离和纵向距离加起来求和,就粗略得出了我们至少应该移动的距离。之后我们在优先队列里,这个距离就是我们判断的依据。
上面这个求距离思想的代码表示
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;
}
再给优先队列设置一下判断的依据(就是重写的这个<号)
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;
就行了
全部代码
#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
;
int fac[] = {1,1,2,6,24,120,720,5040,40320};
int vis
,hehe
;
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;
}
补充一点。
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;
这里的重载<号的判断我们需要深究一下。
return a.r > b.r;是代表我们的优先队列优先把剩下的曼哈顿距离最短的放在队列最前面。
那么上面那个if(a.r==b.r)return a.w > b.w; 呢?就是当曼哈顿距离相等的时候,我们就看我们已经走出去的步数。如果走出去的步数越多,我们更倾向于把他放在队列的最前面。
相关文章推荐
- 八数码第五境界——双向BFS+康托展开判重+回溯记录路径
- HDU 1043 Eight(八数码第七境界|A*+哈希+曼哈顿距离)
- 八数码第四境界——暴力反向BFS+康托展开判重+打表+回溯记录路径
- 【PAT1018】 Public Bike Management 单源最短路径&路径记录回溯
- Pots(bfs+路径记录与回溯)
- sdut oj 3058 路线冲突问题(BFS+记录路径算法,回溯路径 )
- 八数码第八境界——IDA*+逆序数判无解
- POJ1077&HDU1043 Eight 八数码第七境界 AStar hash 康托展开 最小堆优化 奇偶剪枝
- HDOJ 5092 Seam Carving(动态规划,回溯,记录路径)
- 回溯路径的记录
- 【DP】UVA 624 CD 记录路径
- 【团体程序设计天梯赛-练习集L3-015】球队“食物链”【状压dp+路径记录】
- 最近系统整理一下 linux 各配置文件备份、编译命令备份、源文件路径、编译命名路径等记录
- 1、UVA624(记录路径问题)
- uvaoj 116 Unidirectional TSP 动态规划记录字典序最小路径
- 记录学习的点滴(JQuery 路径)
- [转]如何在batch文件中把当前的路径记录在一个变量里,并运用这个变量?
- POJ - 3414 (隐式图搜索+记录路径)
- 递归回溯问题的四道经典题:N皇后,组合,全排列,二叉树路径和
- URAL 1244 Gentlement DP +记录路径 好题