PKU-1077 Eight (八数码之IDA*)

原题链接:http://poj.org/problem?id=1077

Eight

Description

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

 

 

IDA*算法

参考链接:http://blog.csdn.net/urecvbnkuhbh_54245df/article/details/5856756

                  http://www.cnblogs.com/liyongmou/archive/2010/07/19/1780861.html

                  http://www.cnblogs.com/liyongmou/archive/2010/07/16/1778830.html

IDA*算法是A*算法和迭代加深算法的结合。

迭代加深算法是在dfs搜索算法的基础上逐步加深搜索的深度,它避免了广度优先搜索占用搜索空间太大的缺点,也减少了深度优先搜索的盲目性。它主要是在递归搜索函数的开头判断当前搜索的深度是否大于预定义的最大搜索深度,如果大于,就退出这一层的搜索,如果不大于,就继续进行搜索。这样最终获得的解必然是最优解。

而在A*算法中,我们通过使用合理的估价函数,然后在获得的子节点中选择fCost最小的节点进行扩展,以此完成搜索最优解的目的。但是A*算法中,需要维护关闭列表和开放列表,需要对扩展出来的节点进行检测,忽略已经进入到关闭列表中的节点(也就是所谓的“已经检测过的节点”),另外也要检测是否与待扩展的节点重复,如果重复进行相应的更新操作。所以A*算法主要的代价花在了状态检测和选择代价最小节点的排序上,这个过程中占用的内存是比较大的,一般为了提高效率都是使用hash进行重复状态检测。

而IDA*算法具有如下的特点:(

综合了A*算法的人工智能性和回溯法对空间的消耗较少的优点,在一些规模很大的搜索问题中会起意想不到的效果。它的具体名称是 Iterative Deepening A*, 1985年由Korf提出。该算法的最初目的是为了利用深度搜索的优势解决广度A*的空间问题,其代价是会产生重复搜索。归纳一下,IDA*的基本思路是:首先将初始状态结点的H值设为阈值maxH,然后进行深度优先搜索,搜索过程中忽略所有H值大于maxH的结点;如果没有找到解,则加大阈值maxH,再重复上述搜索,直到找到一个解。在保证H值的计算满足A*算法的要求下,可以证明找到的这个解一定是最优解。在程序实现上,IDA* 要比 A* 方便,因为不需要保存结点,不需要判重复,也不需要根据 H值对结点排序,占用空间小。
而这里在IDA*算法中也使用合适的估价函数,来评估与目标状态的距离。

在一般的问题中是这样使用IDA*算法的,当前局面的估价函数值+当前的搜索深度 > 预定义的最大搜索深度时,就进行剪枝。

这个估计函数的选取没有统一的标准,找到合适的该函数并不容易,但是可以大致按照这个原则:在一定范围内加大各个状态启发函数值的差别

 

IDA*
function IDA_star():
    cost_limit = heuristics[rootNode]
 
    while True do
        (solution, cost_limit) = DFS(0, rootNode, cost_limit, [rootNode])
        if solution != None  then
         return (solution, cost_limit)
        if cost_limit == ∞   then
         return None
 
# returns (solution-sequence or None, new cost limit)
function DFS(start_cost, node, cost_limit, path_so_far):
    minimum_cost = start_cost + heuristics[node]
    if minimum_cost > cost_limit  then
       return (None, minimum_cost)
    if node in goalNodes then
       return (path_so_far, cost_limit)
 
    next_cost_limit = ∞
    for succNode in successors[node] do
        newStartCost = start_cost + edgeCosts[(node,succNode)]
        (solution, new_cost_limit) = DFS(newStartCost, succNode, cost_limit, path_so_far + [succNode])
        if solution != None then
         return (solution, new_cost_limit)
        next_cost_limit = min(next_cost_limit, new_cost_limit) 
 
    return (None, next_cost_limit)


Source Code

/*
IDA*算法
*/
#include <iostream>
using namespace std;

#define min(a, b)  a < b ? a : b

char state[9];  //九宫图排列状态
int opration[101];
int dir[4][2] = { 1,0, 0,1, 0,-1, -1,0};  //d, r, l, u
int start_x, start_y;
int maxBound;   //搜索的上限
bool isSolved;  //是否有解决方案


bool isInside(int x, int y) {
	if (x < 0 || y < 0 || x > 2 || y > 2) {
		return false;
	}
	return true;
} 

//计算曼哈顿距离(只计算1~8,确保每次移动h(n)最多减少1,而g(n)会增加1,这样f = g + h为非递减序列)
int goal_state[9][2] = {{0,0}, {0,1}, {0,2}, {1,0}, {1,1}, {1,2}, {2,0}, {2,1}, {2,2}};
int manhattan(const char *board){ 
	int i, x, y, num;
	int dis = 0;
	for (i = 0; i < 9; i++) {
		x = i / 3;
		y = i % 3;
		num = board[i] - '0';
		if (num != 9) {
			dis += (abs(x - goal_state[num - 1][0]) + abs(y - goal_state[num - 1][1]));
		}		
	}
	return dis;
}

//当前maxBound下如果搜不到,则会把搜索过程所有比maxBound大的值中最小的那个重新赋值给maxBound,
//又重头开始搜索,并反复这个过程
int DFS(int x, int y, int dValue, int preOpration) {
	int fValue, hValue, i;
	int new_x, new_y;

	hValue = manhattan(state);
	fValue = hValue + dValue;

	//大于搜索上限,直接返回
	if (fValue > maxBound) {
		return fValue;
	}

	//找到目标状态
	if (hValue == 0) {
		isSolved = true;
		return fValue;
	}

	int nextBound = 1e9;
	for (i = 0; i < 4; i++) {
		if (i + preOpration == 3) {   //剪枝:与上一步移动刚好相反,即回到上一步状态
			continue;
		}

		new_x = x + dir[i][0];
		new_y = y + dir[i][1];

		if (isInside(new_x, new_y)) {
			int oldIndex = x * 3 + y;
			int newIndex = new_x * 3 + new_y;
			swap(state[oldIndex], state[newIndex]);
			opration[dValue] = i;

			int newBound = DFS(new_x, new_y, dValue + 1, i);
			if (isSolved) {
				return newBound;
			}
			nextBound = min(nextBound, newBound);

			swap(state[newIndex], state[oldIndex]);
		}
	}

	return nextBound;  
}

//输出路径
void print_opration() {
	int i;
	if (isSolved) {
		for (i = 0; i < maxBound; i++) {
			switch (opration[i]){
			case 0: 
				printf("d");
				break;
			case 1:
				printf("r");
				break;
			case 2:
				printf("l");
				break;
			case 3:
				printf("u");
				break;
			}
		}
		printf("\n");
	} else {
		printf("unsolvable\n");
	}

}

/*
IDA*的基本思路是:首先将初始状态结点的f值设为阈值maxf,然后进行深度优先搜索,
搜索过程中忽略所有f值大于maxf的结点;如果没有找到解,则加大阈值maxf,再重复上述搜索,直到找到一个解。
在保证f值的计算满足A*算法的要求下,可以证明找到的这个解一定是最优解。在程序实现上,IDA* 要比 A* 方便,因为不需要保存结点,
不需要判重复,也不需要根据f值对结点排序,占用空间小。 
*/
void IDA_Star() {
	isSolved = 0;
	//初始代价
	maxBound = manhattan(state) + 0;

	while (!isSolved && maxBound < 100) {
		maxBound = DFS(start_x, start_y, 0, -10);
	}
}


int main() {
	int i;
	memset(state, 0, sizeof(state));

	for (i = 0; i < 9; i++) {
		cin >> state[i];
		if (state[i] == 'x') {
			state[i] = '0' + 9;
			start_x = i / 3;
			start_y = i % 3;
		}
	}

	IDA_Star();
	print_opration();

	return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值