POJ~1077~八数码~IDA*解题报告

八数码:

题目描述:

The 15-puzzle has been around for over 100 years; even if you don't know it by that name, you've seen it. It is constructed with 15 sliding tiles, each with a number from 1 to 15 on it, and all packed into a 4 by 4 frame with one tile missing. Let's call the missing tile 'x'; the object of the puzzle is to arrange the tiles so that they are ordered as: 
 1  2  3  4 

 5  6  7  8 

 9 10 11 12 

13 14 15  x 

where the only legal operation is to exchange 'x' with one of the tiles with which it shares an edge. As an example, the following sequence of moves solves a slightly scrambled puzzle: 
 1  2  3  4    1  2  3  4    1  2  3  4    1  2  3  4 

 5  6  7  8    5  6  7  8    5  6  7  8    5  6  7  8 

 9  x 10 12    9 10  x 12    9 10 11 12    9 10 11 12 

13 14 11 15   13 14 11 15   13 14  x 15   13 14 15  x 

           r->           d->           r-> 

The letters in the previous row indicate which neighbor of the 'x' tile is swapped with the 'x' tile at each step; legal values are 'r','l','u' and 'd', for right, left, up, and down, respectively. 

Not all puzzles can be solved; in 1870, a man named Sam Loyd was famous for distributing an unsolvable version of the puzzle, and 
frustrating many people. In fact, all you have to do to make a regular puzzle into an unsolvable one is to swap two tiles (not counting the missing 'x' tile, of course). 

In this problem, you will write a program for solving the less well-known 8-puzzle, composed of tiles on a three by three 
arrangement. 

Input:

You will receive a description of a configuration of the 8 puzzle. The description is just a list of the tiles in their initial positions, with the rows listed from top to bottom, and the tiles listed from left to right within a row, where the tiles are represented by numbers 1 to 8, plus ‘x’. For example, this puzzle
1 2 3

x 4 6

7 5 8

is described by this list:

1 2 3 x 4 6 7 5 8

Output:

You will print to standard output either the word ``unsolvable’’, if the puzzle has no solution, or a string consisting entirely of the letters ‘r’, ‘l’, ‘u’ and ‘d’ that describes a series of moves that produce a solution. The string should include no spaces and start at the beginning of the line.。

Sample Input:

 2  3  4  1  5  x  7  6  8 

Sample Output:

ullddrurdllurdruldr

题目大意:

其实这道题很简单理解,就是简单的九宫格排序问题,而只能移动上下左右这四个方向,并且还要满足1 2 3 4 5 6 7 8 x这样的排序即可.

思路分析:

IDA算法是A算法和迭代加深算法的结合。它既有A*的估值函数判断,以及迭代操作避免不必要的搜索。那么题目中我们需要找出一个预定义的最大搜索深度,以此来限制搜索的进行,这里题目给出了九位数字(将x当做9),最坏的情况是在每个位置搜索9次,那么就有81次搜索深度,那么可以四舍五入为100,防止搜索深度不够,在搜索的时候,通过当前局面的估价函数值+当前的搜索深度 > 预定义的最大搜索深度时,就进行剪枝。这里的估值函数运用了曼哈顿距离来判断,下面会给出原理介绍,接下来在搜索所进行的操作请看代码详解:

曼哈顿距离:这里因为是九宫格,所以可以用2点座标的绝对值差
来表示所需要的代价,|x1-x2|+|y1-y2|这里即为上述所讲的h,
将所有h相加即为H。

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<stack>
#include<queue>
#include<cmath>
using namespace std;
typedef struct node
{
	int s[9];//记录元素 
	int loc;//x的位置 
}Lemon;
int di[5][2]={{1,0},{-1,0},{0,1},{0,-1}};//方向变量 
char di1[5]={"durl"};//方向符号 
int dish(int s1[])//曼哈顿距离 
{
	int num=0;
	for(int i=0;i<9;i++)
	{
		if(s1[i]!=9)
		{
			int x=i/3;
			int y=i%3;
			int x1=(s1[i]-1)/3;
			int y1=(s1[i]-1)%3;
			num+=abs(x-x1)+abs(y-y1);
		}
	}
	return num;
}
char c2[1000];
int LemonsIDA(int limit,int depth,int pre,Lemon now)//limit为限制,depth为深度
//pre是记录上次方向(i),防止重复	 
{
	if(dish(now.s)==0)//如果曼哈顿距离为0,说明已经达到123456789 
	{
		c2[depth]='\0'; 
		puts(c2);
		return 1;
	}
	int x=now.loc/3;
	int y=now.loc%3; 
	//得到位置 
	for(int i=0;i<4;i++)//操作方向 
	{
		if(i+pre==1 || i+pre==5)continue;//这一步是为了剪枝,防止走了一步又退一步 
		int x1=x+di[i][0];
		int y1=y+di[i][1];
		if(x1<0 || y1<0 || x1>2 || y1>2)continue;//边界判断 
		else
		{
			Lemon tp=now;
			tp.loc=x1*3+y1;
			tp.s[now.loc]=tp.s[tp.loc];
			tp.s[tp.loc]=9;
			//移动2个模块的数字 
			c2[depth]=di1[i];//记录方向符号 
			if(depth+1+dish(tp.s)<=limit)//判断是否超过限制,通过深度与曼哈顿 
			{
				if(LemonsIDA(limit,depth+1,i,tp)==1)//回溯判断 
				{
					return 1;
				}
			}
		}
	}
	return 0;
}
int main()
{
	Lemon now;
	char c1;
	for(int i=0;i<9;i++)
	{
		cin >> c1;
		if(c1=='x')
		{
			now.s[i]=9;
			now.loc=i;
		}
		else
		{
			now.s[i]=c1-'0';
		}
	}
	int limit;
	for(limit=0;limit<=100;limit++)//这里做一下,因为要移动9个数字,最坏的情况为9!
	//那么每次判断所要移动的次数作为限制,以此来达成递归的边界。 
	{
		if(LemonsIDA(limit,0,2e9,now)==1)
		{
			break;
		}
	}
	if(limit > 100)//如果大于100,说明该数字,无法组成完整的顺序九宫格 
	{
		puts("unsolvable");
	}
	
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章