照上一个题八数码修改来的,只是十五数码和八数码判断是否有解的方法不同,八数码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*都会卡死