8数码的各种版本-搜索
1.基础版
问题 B(1849): 【基础算法】8数码问题版本一
时间限制: 1 Sec 内存限制: 64 MB
题目描述
在一个3*3的九宫格棋盘里,放有8个数码,数码的数字分别是1~8等8个数字。可以通过在九宫格里平移数码来改变状态。数码在任何情况下都不能离开棋盘。给出8个数码的初始状态(没放数码的空格用0表示)和目标状态,问从初始状态到目标状态,最少需要经过多少次移动操作。
例如,初始状态为:
2 6 4
1 3 7
0 5 8
目标状态是:
8 1 5
7 3 6
4 0 2
最少的移动步数为31步。
输入
第1行:9个空格分开的整数,表示初始状态
第2行:9个空格分开的整数,表示目标状态
整数都在0~8范围内,且不重复
输出
第1行:1个整数,表示从初始状态到目标状态的最少移动步数。如果无解,输出-1
样例输入
2 6 4 1 3 7 0 5 8
8 1 5 7 3 6 4 0 2
样例输出
31
提示
Cantor:排列对整数的一一映射
//双向宽搜
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define MAXSTA 362880
const int n=9;
int wt[n+10]={40320,5040,720,120,24,6,2,1,1};
int dir[8]={-1,1,-3,3};
int spos,tpos,p[3][MAXSTA+10];
char s[n+5],t[n+5];
bool vis[3][MAXSTA+10];
struct node{
int step,pos,id,f;
char s[n+5];
node(){}
node(char *str,int a,int b,int c,int e){
strcpy(s,str);
step=a;
pos=b;
id=c;
f=e;
};
};
void read()
{
int x;
for(int i=0;i<n;i++){
scanf("%d",&x);
if(!x) spos=i;
s[i]=x+'0';
}
for(int i=0;i<n;i++){
scanf("%d",&x);
if(!x) tpos=i;
t[i]=x+'0';
}
}
int Cantor(char *str){ //Cantor是一种排列对整数的映射,要先乘大的是因为w[]相当于每一位的进制,每一位上的数应该小于进制。
int ret=0;
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++)
if(str[j]<str[i])
ret+=wt[i];
}
return ret;
}
int DoubleBFS()
{
int num,npos;
char cur[n+5];
node u;
queue<node> que;
num=Cantor(s);
que.push(node(s,0,spos,num,0));
vis[0][num]=true;
num=Cantor(t);
que.push(node(t,0,tpos,num,1));
vis[1][num]=true;
while(!que.empty()){
u=que.front(); que.pop();
strcpy(cur,u.s);
for(int i=0;i<4;i++){
npos=u.pos+dir[i];
if(npos>=0&&npos<n&&(u.pos%3==npos%3||u.pos/3==npos/3)){
swap(cur[u.pos],cur[npos]);
num=Cantor(cur);
if(vis[u.f^1][num])
return p[u.f^1][num]+u.step+1;
else if(!vis[u.f][num]){
p[u.f][num]=u.step+1;
vis[u.f][num]=true;
que.push(node(cur,u.step+1,npos,num,u.f));
}
swap(cur[u.pos],cur[npos]);
}
}
}
return -1;
}
int main()
{
read();
printf("%d\n",DoubleBFS());
}
//IDA*
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
using namespace std;
#define MAXN 9
#define MAXKT 362880
struct node{
char s[MAXN+10];
int step,blank,f;
node(){}
node (char *a,int b,int c,int d){
strcpy(s,a);
step=b;
blank=c;
f=d;
}
bool operator<(const node& y) const{
return f>y.f;
}
};
int pos,w[MAXN+10];
int dir[4+5]={1,-1,3,-3};
char s[MAXN+10],t[MAXN+10];
int f[MAXKT+10],h[MAXKT+10];
void read()
{
int a;
for(int i=0;i<9;i++){
scanf("%d",&a);
s[i]=a+'0';
}
for(int i=0;i<9;i++){
scanf("%d",&a);
t[i]=a+'0';
}
w[8]=1;
for(int i=7;i>=0;i--) w[i]=w[i+1]*(8-i);
memset(f,0x3f,sizeof(f));
}
int kangtuo(char *x)
{
int ret=0;
for(int i=0;i<9;i++){
if(x[i]=='0'){
pos=i; continue;
}
int cnt=0;
for(int j=i+1;j<9;j++)
if(x[j]<x[i])
cnt++;
ret+=cnt*w[i];
}
return ret;
}
int Manhattan_Dist(char *x,char *y)
{
int ret=0;
for(int i=0;i<9;i++){
for(int j=0;j<9;j++)
if(x[i]==y[j]){
ret+=(abs(j-i)/3)+(abs(j-i)%3); ///need to rethink of it
break;
}
}
return ret;
}
void Astar()
{
priority_queue<node> que;
int num=kangtuo(s);
//int diff=Manhattan_Dist(s);
que.push(node(s,0,pos,0));
while(!que.empty()){
node cur=que.top(); que.pop();
char state[MAXN+10];
strcpy(state,cur.s);
num=kangtuo(state);
if(cur.f>f[num]) continue;
for(int i=0;i<4;i++){
int newpos=cur.blank+dir[i];
if(newpos>=0&&newpos<9&&((newpos/3==cur.blank/3)||(newpos%3==cur.blank%3))){
swap(state[cur.blank],state[newpos]);
if(!strcmp(state,t)){
printf("%d\n",cur.step+1);
return ;
}
int more_step=Manhattan_Dist(state,t);
num=kangtuo(state);
if(more_step+cur.step<f[num]){
f[num]=more_step+cur.step+1;
//node next; memcpy(next.s,state,sizeof state);next.step=cur.step+1,next.blank=newpos,next.f=f[num];
que.push(node(state,cur.step+1,newpos,f[num]));
}
swap(state[cur.blank],state[newpos]);
}
}
}
printf("-1\n");
return ;
}
int main()
{
read();
Astar();
return 0;
}
2.版本二:
问题 C(1850): 【基础算法】8数码问题版本二
时间限制: 1 Sec 内存限制: 64 MB
题目描述
15数码问题已经有100多年历史了。与8数码问题类似,它由15个1~15的数码块平铺在一个4*4的棋盘上。数码的初始顺序不确定。我们希望通过平移数码块的方式得一个数码按序排序的目标状态。目标状态通常是这个样子的:(图略)
如果空格用字符’x’表示,则上图的目标状态可用以下的4行4列字符阵列表示:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 x
如果以空格’x’作为移动对象,它可以向4个方向移动,用’r’表示右移,’l’表示左移,’u’表示上移,’d’表示下移。下面给出一个从初始状态到目标状态的移动例子:
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->
似乎任何一个15数码问题都应该有解的,但其实不然。In 1870, a man named Sam Loyd was famous for distributing an unsolvable version of the puzzle, and frustrating many people.
下面这个图就不可解:(图略)
15数码问题的可能状态数有15!个,显然这太大了。于是在版本2中,我们仍然解决的是8数组问题。不过,这一次,我们要求输出从初始状态到固定的目标状态的移动方案。固定的目标状态是1 2 3 4 5 6 7 8 x
本题答案不唯一,所以在搜索扩展结点时,请按照上、左、下、右的顺序。
输入
第1行:9个空格分开的整数,表示8数码的初始状态,空格用小写的’x’表示。
输出
第1行:一个字符串,中间无空格,表示从初始状态到目标状态的移动方案。如果无解,输出”NO SOLUTION”
样例输入
Copy (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)
2 3 4 1 5 x 7 6 8
样例输出
ullddrurdllurdruldr
提示
由于要按照顺序来找解,双向,A*都不行了,只能做最普通的BFS
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
using namespace std;
#define MAXSTA 362880
#define MAXSP 1000000
typedef long long LL;
const int n=9;
int w[20]={40320,5040,720,120,24,6,2,1,1};
int dir[10]={-3,-1,3,1};
char r[10]={'u','l','d','r'};
int spos,tpos,tnum;
bool vis[MAXSTA+10];
char s[n+5],t[n+5],ans[MAXSP+10];
struct node{
char s[n+5];
int step,pos,id;
LL path;
node(){}
node(char *a,int b,int c,int d,LL g){
strcpy(s,a);
step=b;
pos=c;
id=d;
path=g;
}
};
void read()
{
char str[n+5];
for(int i=0;i<n;i++){
scanf("%s",str);
if(str[0]!='x')
s[i]=str[0];
else{
s[i]='0';
spos=i;
}
}
for(int i=0;i<n-1;i++)
t[i]=i+'0'+1;
t[n-1]='0';
tpos=n-1;
}
int Cantor(char *str)
{
int ret=0;
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++)
if(str[j]<str[i])
ret+=w[i];
}
return ret;
}
void print(LL x,int T)
{
memset(ans,0,sizeof ans);
while(T--){
ans[T]=r[x%4];
x/=4;
}
printf("%s\n",ans);
}
void BFS()
{
tnum=Cantor(t);
node u;
queue<node> que;
int num=Cantor(s),npos;
char cur[n+5];
que.push(node(s,0,spos,num,0));
vis[num]=true;
while(!que.empty()){
u=que.front(); que.pop();
//vis[u.id]=false;
strcpy(cur,u.s);
for(int i=0;i<4;i++){
npos=u.pos+dir[i];
if((npos>=0&&npos<9)&&(npos%3==u.pos%3||npos/3==u.pos/3)){
swap(cur[npos],cur[u.pos]);
num=Cantor(cur);
if(!strcmp(cur,t)){
//printf("%s %d\n",cur,u.step+1);
print(u.path*4+i,u.step+1);
return ;
}
if(vis[num]){
swap(cur[npos],cur[u.pos]);
continue;
}
vis[num]=true;
que.push(node(cur,u.step+1,npos,num,u.path*4+i));
swap(cur[npos],cur[u.pos]);
}
}
}
printf("NO SOLUTION\n");
}
bool check()
{
int ta=0,tb=0;
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++){
if(s[i]>s[j]&&s[j]!='0')
ta++;
if(t[i]>t[j]&&t[j]!='0')
tb++;
}
if((ta&1)==(tb&1))
return true;
else
return false;
} //每次移动都是swap前后的两个数,如果s,t的逆序奇偶性不同是怎么都不可能成功的。
int main()
{
read();
if(!check()){
printf("NO SOLUTION\n");
return 0;
}
BFS();
}
3.版本三
问题 D(2627): 8数码问题版本三
时间限制: 1 Sec 内存限制: 128 MB
题目描述
在一个3*3的九宫格棋盘里,放有8个数码,数码的数字分别是1~8等8个数字。可以通过在九宫格里平移数码来改变状态。数码在任何情况下都不能离开棋盘。给出8个数码的初始状态(没放数码的空格用0表示)和目标状态,问从初始状态到目标状态,最少需要经过多少次移动操作。
例如,初始状态为:
2 6 4
1 3 7
0 5 8
目标状态是:
8 1 5
7 3 6
4 0 2
最少的移动步数为31步。
这一次,我们要解决的问题有所不同:给出初始状态,找出需要移动步数最多的状态,以及移动的方案。
输入
共3行,每行3个整数,表示初始状态
输出
前3行,每行3个整数,表示移动步数最多的目标状态
第4行:一个字符串,表示移动的方案。U表示0向上,D表示0向下,L表示0向左,R表示0向右。
样例输入
2 6 4
1 3 7
0 5 8
样例输出
8 1 5
7 3 6
4 0 2
UURDDRULLURRDLLDRRULULDDRUULDDR
题意:对于每个状态的最小步数中的最大值,还是只有暴搜(因为要搜完所有state,直接搜比较好)
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long LL;
#define MAXSTA 362880
const int n=9;
int w[20]={40320,5040,720,120,24,6,2,1,1};
int dir[10]={-3,3,-1,1};
char r[10]={'U','D','L','R'};
int dist[MAXSTA+10],spos,ans;
bool vis[MAXSTA+10];
char s[n+5],res[n+5];
LL route[MAXSTA+10];
struct node{
char s[n+5];
int step,pos,id;
LL path;
node(){}
node(char *a,int b,int c,int d,LL e){
strcpy(s,a);
step=b;
pos=c;
id=d;
path=e;
}
};
void read()
{
int x;
for(int i=0;i<n;i++){
scanf("%d",&x);
s[i]=x+'0';
if(!x) spos=i;
}
}
int Cantor(char *str)
{
int ret=0;
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
if(str[j]<str[i])
ret+=w[i];
return ret;
}
void BFS()
{
node u;
queue<node> que;
int num=Cantor(s),npos;
char cur[n+5];
que.push(node(s,0,spos,num,0));
vis[num]=true;
dist[num]=0,route[num]=0;
ans=MAXSTA+2;
while(!que.empty()){
u=que.front(); que.pop();
if(dist[u.id]>=dist[ans])
ans=u.id;
strcpy(cur,u.s);
for(int i=0;i<4;i++){
npos=u.pos+dir[i];
if((npos>=0&&npos<9)&&(npos%3==u.pos%3||npos/3==u.pos/3)){
swap(cur[npos],cur[u.pos]);
num=Cantor(cur);
if(!vis[num]){
vis[num]=true;
dist[num]=u.step+1;
route[num]=u.path*4+i;
que.push(node(cur,u.step+1,npos,num,u.path*4+i));
}
swap(cur[npos],cur[u.pos]);
}
}
}
}
void print(int id,LL x,int T)
{
memset(vis,0,sizeof vis);
for(int i=0;i<n;i++){
int cnt=1;
while(id>=w[i])
id-=w[i],cnt++;
for(int j=0;j<n;j++){
if(!vis[j])
cnt--;
if(!cnt){
printf("%d",j);
vis[j]=true;
break;
}
}
if(i%3==2) printf("\n");
else printf(" ");
}
memset(res,0,sizeof res);
while(T--){
res[T]=r[x%4];
x/=4;
}
printf("%s\n",res);
}
int main()
{
read();
BFS();
print(ans,route[ans],dist[ans]);
}