POJ 1077八数码问题(cantor展开+BFS)
2016-04-28 11:06
639 查看
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:
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:
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
is described by this list:
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
Sample Output
广度搜索很好想,主要难想的地方在hash判重,这里用到的是cantor展开判重复
基本思想是,从全排列组成的数字中,找出比他小的有数有多少
对于一个数字,从第一位看起,找他后面有多少个比这一位小的位,比如有3个,那就是假设如果交换
这两位那么后面的数无论怎么排列都比以前小,也就是比原来那个数小的数有剩下位数全排列种类数的个数。
比如213456789
第一位2后面有一个数1比他小,假设交换
1(23456789)括号里面的数无论怎么排列都比原来的213456789小吧
那就是8!种
再比如312456789
第一位3后面有两个数1和2比他小,假设交换
1(32456789)括号里面的数无论怎么排列都比原来的312456789小吧
2(13456789)括号里面的数无论怎么排列都比原来的312456789小吧
那就是8!+8!种
其他位数也是都是这样运算的。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
struct node {
int a[3][3] ;
int x,y ;
char states[100];
node(){
memset(states,'\0',sizeof(states));
memset(a,0,sizeof(a));
x=0;y=0;
}
};
node h;
const int MAXN=1000000;///最多是9!
int fac[]={1,1,2,6,24,120,720,5040,40320,362880};///康拖展开判重0!1!2!3!4!5!6!7!8!9!
bool vis[MAXN];///标记
int addx[4]={-1,1,0,0};
int addy[4]={0,0,-1,1};
int cantor(int m[3][3])///康拖展开求该序列的hash值
{
int s[9];
int k=0;
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
s[k++]=m[i][j];
}
}
int sum=0;
for(int i=0;i<9;i++)
{
int num=0;
for(int j=i+1;j<9;j++)
if(s[j]<s[i])num++;
sum+=(num*fac[9-i-1]);
}
return sum+1;
}
void bfs(){
queue<node> qq;
qq.push(h);
while(!qq.empty()){
node top=qq.front();
qq.pop();
if(cantor(top.a)==1){
printf("%s\n",top.states);
return ;
}
int ch[3][3];
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
ch[i][j]=top.a[i][j];
}
}
for(int i=0;i<4;i++){
int newx=top.x+addx[i];
int newy=top.y+addy[i];
if(newx>=0&&newx<3&&newy>=0&&newy<3){
swap(ch[newx][newy],ch[top.x][top.y]);
if(!vis[cantor(ch)]){
node pp;
memcpy(pp.a,ch,9*sizeof(int));///快速复制数组
memcpy(pp.states,top.states,sizeof(top.states));
pp.x=newx;
pp.y=newy;
if(i==0)strcat(pp.states,"u");
if(i==1)strcat(pp.states,"d");
if(i==2)strcat(pp.states,"l");
if(i==3)strcat(pp.states,"r");
qq.push(pp);
vis[cantor(pp.a)]=true;
}
swap(ch[newx][newy],ch[top.x][top.y]);
}
}
}
}
int main(){
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int arry[9];
for(int i=0;i<9;i++){
char c[2];
scanf("%s",&c);
if(c[0]=='x')arry[i]=9;
else arry[i]=c[0]-'0';
}
int k=0;
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
h.a[i][j]=arry[k++];
if(h.a[i][j]==9){
h.x=i;
h.y=j;
}
}
}
memset(vis,false,sizeof(vis));
vis[cantor(h.a)]=true;
bfs();
return 0;
}
Time Limit: 1000MS | Memory Limit: 65536K | |||
Total Submissions: 28887 | Accepted: 12584 | Special Judge |
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
广度搜索很好想,主要难想的地方在hash判重,这里用到的是cantor展开判重复
基本思想是,从全排列组成的数字中,找出比他小的有数有多少
对于一个数字,从第一位看起,找他后面有多少个比这一位小的位,比如有3个,那就是假设如果交换
这两位那么后面的数无论怎么排列都比以前小,也就是比原来那个数小的数有剩下位数全排列种类数的个数。
比如213456789
第一位2后面有一个数1比他小,假设交换
1(23456789)括号里面的数无论怎么排列都比原来的213456789小吧
那就是8!种
再比如312456789
第一位3后面有两个数1和2比他小,假设交换
1(32456789)括号里面的数无论怎么排列都比原来的312456789小吧
2(13456789)括号里面的数无论怎么排列都比原来的312456789小吧
那就是8!+8!种
其他位数也是都是这样运算的。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
struct node {
int a[3][3] ;
int x,y ;
char states[100];
node(){
memset(states,'\0',sizeof(states));
memset(a,0,sizeof(a));
x=0;y=0;
}
};
node h;
const int MAXN=1000000;///最多是9!
int fac[]={1,1,2,6,24,120,720,5040,40320,362880};///康拖展开判重0!1!2!3!4!5!6!7!8!9!
bool vis[MAXN];///标记
int addx[4]={-1,1,0,0};
int addy[4]={0,0,-1,1};
int cantor(int m[3][3])///康拖展开求该序列的hash值
{
int s[9];
int k=0;
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
s[k++]=m[i][j];
}
}
int sum=0;
for(int i=0;i<9;i++)
{
int num=0;
for(int j=i+1;j<9;j++)
if(s[j]<s[i])num++;
sum+=(num*fac[9-i-1]);
}
return sum+1;
}
void bfs(){
queue<node> qq;
qq.push(h);
while(!qq.empty()){
node top=qq.front();
qq.pop();
if(cantor(top.a)==1){
printf("%s\n",top.states);
return ;
}
int ch[3][3];
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
ch[i][j]=top.a[i][j];
}
}
for(int i=0;i<4;i++){
int newx=top.x+addx[i];
int newy=top.y+addy[i];
if(newx>=0&&newx<3&&newy>=0&&newy<3){
swap(ch[newx][newy],ch[top.x][top.y]);
if(!vis[cantor(ch)]){
node pp;
memcpy(pp.a,ch,9*sizeof(int));///快速复制数组
memcpy(pp.states,top.states,sizeof(top.states));
pp.x=newx;
pp.y=newy;
if(i==0)strcat(pp.states,"u");
if(i==1)strcat(pp.states,"d");
if(i==2)strcat(pp.states,"l");
if(i==3)strcat(pp.states,"r");
qq.push(pp);
vis[cantor(pp.a)]=true;
}
swap(ch[newx][newy],ch[top.x][top.y]);
}
}
}
}
int main(){
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int arry[9];
for(int i=0;i<9;i++){
char c[2];
scanf("%s",&c);
if(c[0]=='x')arry[i]=9;
else arry[i]=c[0]-'0';
}
int k=0;
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
h.a[i][j]=arry[k++];
if(h.a[i][j]==9){
h.x=i;
h.y=j;
}
}
}
memset(vis,false,sizeof(vis));
vis[cantor(h.a)]=true;
bfs();
return 0;
}
相关文章推荐
- Python爬虫之Pixiv
- eclipse怎么样新建web项目,eclipse新建web项目
- Android初学习 – 带有动态库、静态库、Jar包的makefile编写
- Oracle sql日期比较和常用日期函数整理(全)
- Android百度地图深入学习(1)
- Android.mk文件语法规范与变量详细说明
- 《硅谷之谜》文摘
- 插入排序算法
- 阿里 Dubbo架构设计攻略
- 高速PCB设计绕等长一定要绕个山路十八弯太算吊?
- 阿里 Dubbo架构设计攻略
- nyoj714 Card Trick(第六届河南省程序设计大赛)
- leetcode 两个数相除,不用乘除和mode
- Docker入门笔记_1_安装docker
- C++ Virtual详解
- Windows下安装Mysql-5.7.12
- 适配器模式
- PHP 面向对象
- HDU 2577 How to Type
- 今日BBC