照上一个题八数码修改来的,只是十五数码和八数码判断是否有解的方法不同,八数码0的移动不影响其余7个数字逆序数的奇偶性,而十五数码0的左右移动不影响其余15个数逆序数的奇偶性(顺序不变),但上下移动改变奇偶(移动三次),加上0的话16个数逆序数左右改变(移动一次),上下也改变(移动7次),需要注意每次移动0的距离奇偶性也改变(0到目标位置的曼哈顿距离不是加1就是减一),所以16个数逆序数与0的距离之和s的奇偶性不因0的滑动而改变,初始时s是奇数,所以只有s是奇数的状态才是可到达的
#include<stdio.h> #include<string.h> #include<math.h> #define size 4 int move[4][2]={{-1,0},{0,-1},{0,1},{1,0}};//上 左 右 下 char op[4]={'U','L','R','D'}; int map[size][size],map2[size*size],limit,path[100]; int flag,length; //int goal_st[3][3]={{1,2,3},{4,5,6},{7,8,0}}; int goal[16][2]= {{3,3},{0,0},{0,1}, {0,2},{0,3}, {1,0}, {1,1}, {1,2}, {1,3},{2,0}, {2,1}, {2,2},{2,3},{3,0},{3,1},{3,2}};;//目标位置 int nixu(int a[size*size]) { int i,j,ni,w,x,y; ni=0; for(i=0;i<size*size;i++) { if(a[i]==0) w=i; for(j=i+1;j<size*size;j++) { if(a[i]>a[j]) ni++; } } x=w/size; y=w%size; ni+=abs(x-3)+abs(y-3); if(ni%2==1) return 1; else return 0; } int hv(int a[][size])//估价函数,曼哈顿距离,小等于实际总步数 { int i,j,cost=0; for(i=0;i<size;i++) { for(j=0;j<size;j++) { int w=map[i][j]; cost+=abs(i-goal[w][0])+abs(j-goal[w][1]); } } return cost; } void swap(int*a,int*b) { int tmp; tmp=*a; *a=*b; *b=tmp; } void dfs(int sx,int sy,int len,int pre_move)//sx,sy是空格的位置 { int i,j,nx,ny; if(flag) return; int dv=hv(map); if(len==limit) { if(dv==0) { flag=1; length=len; return; } else return; } else if(len<limit) { if(dv==0) { flag=1; length=len; return; } } for(i=0;i<4;i++) { if(i+pre_move==3&&len>0)//不和上一次移动方向相反,对第二步以后而言 continue; nx=sx+move[i][0]; ny=sy+move[i][1]; if(0<=nx&&nx<size && 0<=ny&&ny<size) { swap(&map[sx][sy],&map[nx][ny]); int p=hv(map); if(p+len<=limit&&!flag) { path[len]=i; dfs(nx,ny,len+1,i); if(flag) return; } swap(&map[sx][sy],&map[nx][ny]); } } } int main() { int i,j,k,l,m,n,sx,sy; char c,g; i=0; scanf("%d",&n); while(n--) { flag=0;length=0; memset(path,-1,sizeof(path)); for(i=0;i<16;i++) { scanf("%d",&map2[i]); if(map2[i]==0) { map[i/size][i%size]=0; sx=i/size;sy=i%size; } else { map[i/size][i%size]=map2[i]; } } if(nixu(map2)==1)//该状态可达 { limit=hv(map); while(!flag&&length<=50)//题中要求50步之内到达 { dfs(sx,sy,0,0); if(!flag) limit++; //得到的是最小步数 } if(flag) { for(i=0;i<length;i++) printf("%c",op[path[i]]); printf("\n"); } } else if(!nixu(map2)||!flag) printf("This puzzle is not solvable.\n"); } return 0; } /* 2 2 3 4 0 1 5 7 8 9 6 10 12 13 14 11 15 13 1 2 4 5 0 3 7 9 6 10 12 15 8 11 14 LLLDRDRDR This puzzle is not solvable. */十五数码的用bfd和A*都会卡死