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]);
}

转载于:https://www.cnblogs.com/katarinayuan/p/6572841.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值