HDU - 1043 Eight(八数码+康拓展开式+BFS暴力打表)

Eight
Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Special Judge

Problem 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 1213 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, several descriptions of configuration of the 8 puzzle. One 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. Do not print a blank line between cases.
Sample Input
2  3  4  1  5  x  7  6  8
Sample Output

ullddrurdllurdruldr


首先对康拓展开式做简单说明

任何一个字符串都有N中排列情况,而康拓展开式便可以计算出一个字符串出现在它按字典序的排列中的第几位。

如1 3 2求康拓展开式等于2。说明1 3 2在全排列的字典序中排第三。

如此就可以对一个字符串建立哈希表,利用康拓展开式便可求出其键值。

附上康拓展开式的具体求法点击打开链接

八数码问题嘛,枚举所有的状态才36w多种的情况。

直接从最正确的状态开始枚举,把所有状态都暴力打成表(树形),最后只要给出一个状态,通过康拓展开找到它的位置,回溯回去找根节点,如果能找到说明该状态是从根节点衍生出来的一定可以回溯回去。如果无法回溯说明该节点无法从根节点衍生出来。

#include <iostream>
#include <cstring>
#include <queue>
#include <stack>
#include <map>
using namespace std;
char str[50];
int buf[10],go[4][2] = {0,1,0,-1,1,0,-1,0};
long fac[9];
int m,n;

struct node{
	char dir;//记录方向 
	long father;//记录上一个节点 
}que[370000];//用数组储存树形结构 

struct node1{
	int map[10];
	long por;//代表当前字符串的的位置(位置右康拓展开式决定) 
	int n;//n是x在字符串中的位置。
};
int set_fac(int a)//计算阶乘 
{
	long ans = 1;
	for (int i = 1;i <= a; i++){	
		ans *= i;
	}
	return ans;
}
long cantor(int *buf)//康拓展开 
{
	long ans = 0;
	for (int i = 0;i < 9; i++){
		int por = 0;
		for (int j = i+1;j < 9; j++){
			if (buf[i] > buf[j])
				por++;
		}
		ans += por * fac[8-i];
	}
	return ans;
}

void bfs()
{
	node1 p,q;
	queue <node1> Q;
	for (int i = 0; i < 9;i++){
		p.map[i] = i+1;//初始化队列中第一个节点,其排列状态便是最正确的状态,再从这个状态去衍生出所有情况从而暴力打表 
	}
	p.n = 8;
	p.por = 0;
	que[p.por].father = 0;
	Q.push(p);
	while (!Q.empty()){
		q = Q.front();
		Q.pop();
		for (int i = 0;i < 4; i++){
			p = q;
			int tx = q.n/3+go[i][0],ty = q.n%3+go[i][1];//坐标分解 
			if (tx >= 0 && ty >= 0 && tx < 3 && ty < 3){
				int tem;
				p.n = tx*3 + ty;//坐标合并 
				tem = p.map[p.n];//交换 
				p.map[p.n] = p.map[q.n];
				p.map[q.n] = tem;//交换 
				p.por = cantor(p.map);//计算康拓值 
				if (que[p.por].father == -1){
					switch(i){//这里假如当i==0的时候,其实是向右,但是由于查表的时候是反正查的,所以这里记录的方向也要相反方便查表
						case 0:que[p.por].dir = 'l';break;
						case 1:que[p.por].dir = 'r';break;
						case 2:que[p.por].dir = 'u';break;
						case 3:que[p.por].dir = 'd';break;
					}
					que[p.por].father = q.por;//当前节点的父节点就是上一节点的康拓值 
					Q.push(p);
				}
			}
		}
	}
}
int main ()
{
	for (int i = 0;i < 9; i++){
		fac[i] = set_fac(i);
	}
	for (long i = 0;i < 370000; i++){
		que[i].father = -1;
	}
	bfs();
	while (gets(str) != NULL){
		for (int i = 0,j = 0;str[i]; i++){//将字符串转化为int型。 
			if (str[i] == 'x'){
				buf[j++] = 9;
			}
			else{
				if (str[i] >= '0' && str[i] <= '8'){
					buf[j++] = str[i] - '0';
				}
			}
		}
		long s = cantor(buf);
		if (que[s].father == -1){//树中每一个节点的状态都是从正确状态变换而来,不在树内说明无法反向回到根节点达成目标。
			printf ("unsolvable\n");
		}
		else{
			while (s){
				printf ("%c",que[s].dir);
				s = que[s].father;
			}
			printf ("\n");
		}
	}
	return 0;
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值