八数码管 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); // 移除选做当前位的数
}
}

浙公网安备 33010602011771号