uva 10181 - 15-Puzzle Problem 十五数码 IDA*

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


  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是C语言实现15 Puzzle问题的IDA*(DFS策略与曼哈顿距离启发)的代码: ``` #include<stdio.h> #include<string.h> #include<stdlib.h> #define MAXN 16 //目标状态 int goal[MAXN]={ 0,1,2,3, 4,5,6,7, 8,9,10,11, 12,13,14,15 }; //每个方向的移动 int dx[4]={-1,0,1,0}; int dy[4]={0,1,0,-1}; char dir[4]={'u','r','d','l'}; //启发函数,曼哈顿距离 int h(int *state){ int sum=0; for(int i=0;i<16;i++){ if(state[i]==0)continue; int x1=i/4,y1=i%4; int x2=(state[i]-1)/4,y2=(state[i]-1)%4; sum+=abs(x1-x2)+abs(y1-y2); } return sum; } //深度优先搜索 int dfs(int dep,int last,int *state,int limit,char *path){ int f=dep+h(state); if(f>limit)return f; if(memcmp(state,goal,sizeof(goal))==0)return 0; int x,y,tx,ty; x=last/4,y=last%4; for(int i=0;i<4;i++){ tx=x+dx[i],ty=y+dy[i]; if(tx<0||tx>=4||ty<0||ty>=4)continue; //剪枝 if(dep>0&&path[dep-1]==dir[i])continue; int t=tx*4+ty; swap(state[last],state[t]); path[dep]=dir[i]; int d=dfs(dep+1,t,state,limit,path); if(d==0)return 0; swap(state[last],state[t]); } return f; } int main(){ int state[MAXN]; char path[MAXN]; for(int i=0;i<16;i++){ scanf("%d",&state[i]); if(state[i]==0)state[i]=16; } int limit=h(state); while(dfs(0,0,state,limit,path)!=0){ limit++; } path[limit]='\0'; printf("%s\n",path); return 0; } ``` 这个程序使用深度优先搜索算法来解决15 Puzzle问题,并且使用曼哈顿距离作为启发函数来帮助加速搜索。在搜索过程中,使用剪枝来避免重复搜索。最后,程序输出解决问题的路径。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值