八数码管 Hdu 1043

八数码管Hdu 1043

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, 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

Hdu - 1043

使用set 超时

在原来的基础上使用康诺超时

最后使用反向bfs加康诺正确

#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<set>
#include<string>
#include<cstring>
//先使用set解决
using namespace std;
const  int maxn =362880; 
int puzzle[10],goal[10];
string path[maxn];
int book[maxn];    // 阶乘
struct node {
	int st[10];   // 状态
	int z;
	string dis;
	
	node(int *cp,int z1,string dis1) {
		memcpy(st,cp,sizeof(puzzle));
		z=z1;
		dis=dis1;
	}
};     //  结点保留 路径


int dx[]= {1,0,0,-1};
int dy[] = {0,-1,1,0};
char dz[]={'u','r','l','d'};


//康诺
static const int FAC[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
int contor(int *a){
	int x=0;
	for(int i=0;i<9;i++){
		int smaller=0;
		for(int j=i+1;j<9;j++){
			if(a[j]<a[i])
				smaller++;
		}
		x+=FAC[8-i]*smaller;
	}
	return x;
}
void init_lookup() {
	memset(book,0,sizeof(book));
	book[contor(puzzle)]=1;
}

// set处理
//set<int> vis;
//void init_lookup() {
//	vis.clear();
//	int temp=puzzle[0];
//	for(int i=1; i<9; i++)
//		temp = temp*10+puzzle[i];
//	vis.insert(temp);
//}
//int lookvis(int code) {
//	if(vis.count(code)){
//		return 0;
//	} 
//	else {
//		vis.insert(code); 
//		return 1;
//	}
//}

void bfs() {
	queue<node> q;

	q.push(node(goal,8,""));
	// 使用bfs搜索
	init_lookup();
	while(!q.empty()) {
		node cur = q.front();
		q.pop();
	//	for(int i=0;i<3;i++){
//		
//		for(int j=0;j<3;j++)
//			cout<<cur.st[i*3+j]<<" ";
//		cout<<endl;
//		}
//		cout<<"路径为:"<<cur.dis<<" 步长是"<<cur.dis.size()<<endl;
		int z=cur.z;
		
		// 向四个方向
		for(int i=0; i<4; i++) {

			int newx= z/3+dx[i];   // 数组外层
			int newy= z%3+dy[i];   // 数组内层
			int newz= newx*3+newy;	// 新数组下标

			if(newx>=0&&newy>=0&&newx<3&&newy<3) { //下标越界
				node newnode=cur;
				newnode.z=newz;
				newnode.dis=dz[i]+newnode.dis;
				int temp =newnode.st[z];
				newnode.st[z]=newnode.st[newz];
				newnode.st[newz]=temp;
				int code= contor(newnode.st); 
				if(!book[code])  {
					book[code]=1;
					q.push(newnode);
					path[code]=newnode.dis;
				}

				}
		}
	}
	return ;
}



int main() {
	
	for(int i=0;i<8;i++)
		goal[i]=i+1;
	goal[8]=0;
	bfs();
	freopen("HDU1043.txt","r",stdin);
	char c;
	while(~scanf(" %c",&c)){
		
		if(c=='x'){
			puzzle[0]=0;
		}else{
			puzzle[0]=c-'0';
		}

		for(int i=1; i<9; i++) {
			char temp;
			scanf(" %c",&temp);
			if(temp=='x')
				puzzle[i]=0;
			else
				puzzle[i]=temp-'0';
		}
		int hash=contor(puzzle);
		if(!book[hash]){
			cout<<"unsolvable"<<endl;
		}else{
			cout<<path[hash]<<endl;
		}
	}
	return 0;
}
康诺展开

康托展开是一个全排列到一个自然数的双射,常用于构建hash表时的空间压缩。设有n个数(1,2,3,4,…,n),可以有组成不同(n!种)的排列组合,康托展开表示的就是是当前排列组合在n个不同元素的全排列中的名次。

static const int FAC[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};    // 阶乘
int cantor(int *a, int n)
{
    int x = 0;
    for (int i = 0; i < n; ++i) {
        int smaller = 0;  // 在当前位之后小于其的个数
        for (int j = i + 1; j < n; ++j) {
            if (a[j] < a[i])
                smaller++;
        }
        x += FAC[n - i - 1] * smaller; // 康托展开累加
    }
    return x;  // 康托展开值
}
康托逆展开
static const int FAC[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};    // 阶乘

//康托展开逆运算
void decantor(int x, int n)
{
    vector<int> v;  // 存放当前可选数
    vector<int> a;  // 所求排列组合
    for(int i=1;i<=n;i++)
        v.push_back(i);
    for(int i=m;i>=1;i--)
    {
        int r = x % FAC[i-1];
        int t = x / FAC[i-1];
        x = r;
        sort(v.begin(),v.end());// 从小到大排序 
        a.push_back(v[t]);      // 剩余数里第t+1个数为当前位
        v.erase(v.begin()+t);   // 移除选做当前位的数
    }
}
posted @ 2020-04-08 16:06  痴痴那  阅读(116)  评论(0)    收藏  举报