【洛谷P1379】八数码难题(广搜、A*)

八数码难题

题目描述

 

一.广搜:

首先要考虑用什么存每一个状态

显然每个状态都用一个矩阵存是很麻烦的。

我们可以考虑将一个3*3的矩阵用一个字符串或long long 存。

每次扩展时再转化为矩阵。

另外一个问题是判重,对于已经搜过的状态,就不再扩展了。

10^9次方的bool数组会爆空间

可以考虑用hash

或者STL的mapset

//map   洛谷 8964ms 
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;
map<long long ,long long> m;
long long s,que[10000010],head=1,tail=1;
long long c[4][4];
long long zx[5]={0,0,0,1,-1},
          zy[5]={0,1,-1,0,0};
int main()
{
    scanf("%lld",&s);
    m[s]=0;
    que[head]=s;
    while(head<=tail)        //广搜 
    {
        int x,y;
        long long u=que[head++];    //取出队首元素 
        long long uu=u;
        if(u==123804765) break;
        for(int i=1;i<=3;i++)    //将long long型转化为矩阵 
         for(int j=1;j<=3;j++)
         {
             c[i][j]=uu%10;
             if(c[i][j]==0){        //标记空格 
                 x=i;
                 y=j;
             }
             uu/=10;
         }
        for(int i=1;i<=4;i++)
        {
            long long xx=x+zx[i],yy=y+zy[i];    //向四个方向搜索 
            if(1<=xx&&xx<=3&&1<=yy&&yy<=3)
            {
                swap(c[x][y],c[xx][yy]);
                long long l=0;
                for(int j=3;j>=1;j--)
                 for(int k=3;k>=1;k--)
                  l=l*10+c[j][k];            //将扩展状态转化为长整形 
                map < long long ,long long > :: iterator it = m.find(l) ;    //判重   map中的find函数用于寻找象所对应的原象(键值),若查找失败,返回最后一个键值位置 
                if(it==m.end()){
                    m[l]=m[u]+1;
                    que[++tail]=l;
                }
                swap(c[x][y],c[xx][yy]);
            }
        }
    }
    printf("%lld
",m[123804765]);
    return 0;
}
//hash   洛谷 1080ms 
#pragma GCC optimize(3)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MOD=23333333;
long long s,que[10000010],time[10000010],head=1,tail=1;
long long c[4][4];
long long zx[5]={0,0,0,1,-1},
          zy[5]={0,1,-1,0,0};
bool hashnum[100000000];
inline bool hash(long long s)
{
    long long h=1,i=1;
    while(s)
    {
        i++;
        long long k=s%10;
        s/=10;
        h=(h*i*233+k)%MOD;
    }
    if(!hashnum[h]){
        hashnum[h]=1;
        return 1;
    }
    else return 0;
}
int main()
{
    scanf("%lld",&s);
    que[head]=s;
    while(head<=tail)
    {
        int x,y;
        long long u=que[head++];
        long long uu=u;if(u==123804765) break;
        for(register int i=1;i<=3;i++)
         for(register int j=1;j<=3;j++)
         {
             c[i][j]=uu%10;
             if(c[i][j]==0){    
                 x=i;
                 y=j;
             }
             uu/=10;
         }
        for(register int i=1;i<=4;i++)
        {
            long long xx=x+zx[i],yy=y+zy[i];
            if(1<=xx&&xx<=3&&1<=yy&&yy<=3)
            {
                swap(c[x][y],c[xx][yy]);
                long long l=0;
                for(int j=3;j>=1;j--)
                 for(int k=3;k>=1;k--)
                  l=l*10+c[j][k];
                if(hash(l)){
                    que[++tail]=l;
                    time[tail]=time[head-1]+1;
                }
                swap(c[x][y],c[xx][yy]);
            }
        }
    }
    printf("%lld
",time[head-1]);
    return 0;
}

二、启发式搜索

 估价函数:

close[i]=time[i]+cym[i]

time[i]是搜到当前状态已经用的时间

cym[i]表示每个数字到目标状态的曼哈顿距离之和/2

可以看出,close[i]是小于实际步数的,所以搜起来效率高些

//启发式搜索 420ms 
#pragma GCC optimize(3)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
const int MOD=23333333;
long long s,num;
long long data[10000010];
int time[10000010];
int close[10000010];
struct cmp
{
    bool operator()(int a,int b)
    {
        return close[a]>close[b];
    }
};
priority_queue<int,vector<int>,cmp> que;
long long c[4][4];
long long zx[5]={0,0,0,1,-1},
          zy[5]={0,1,-1,0,0};
bool hashnum[100000000];
inline bool hash(long long s)
{
    long long h=1,i=1;
    while(s)
    {
        i++;
        long long k=s%10;
        s/=10;
        h=(h*i*233+k)%MOD;
    }
    if(!hashnum[h]){
        hashnum[h]=1;
        return 1;
    }
    else return 0;
}
int de[9][2]={{2,2},{1,1},{1,2},{1,3},{2,3},{3,3},{3,2},{3,1},{2,1}};
inline int cym(long long tt)    //个位数字的笛卡尔距离之和 
{
    int nn[4][4];
    for(register int i=0;i<3;i++)
         for(register int j=0;j<3;j++)
         {
             nn[3-i][3-j]=tt%10;
             tt/=10;
         }
    int mark=0;
    for(register int i=1;i<=3;i++)
     for(register int j=1;j<=3;j++)
         mark+=abs(i-de[nn[i][j]][0])+abs(j-de[nn[i][j]][1]);
    return mark>>1;
}
int main()
{
    scanf("%lld",&s);
    data[++num]=s;
    close[num]=cym(s);
    que.push(num);
    int u;
    while(!que.empty())
    {
        int x,y;
        u=que.top();            //每次取估价最小的元素 
        que.pop();
        long long uu=data[u];if(uu==123804765) break;
        for(register int i=1;i<=3;i++)
         for(register int j=1;j<=3;j++)
         {
             c[i][j]=uu%10;
             if(c[i][j]==0){    
                 x=i;
                 y=j;
             }
             uu/=10;
         }
        for(register int i=1;i<=4;i++)
        {
            long long xx=x+zx[i],yy=y+zy[i];
            if(1<=xx&&xx<=3&&1<=yy&&yy<=3)
            {
                swap(c[x][y],c[xx][yy]);
                long long l=0;
                for(int j=3;j>=1;j--)
                 for(int k=3;k>=1;k--)
                  l=l*10+c[j][k];
                 if(hash(l)){
                    data[++num]=l;
                    time[num]=time[u]+1;
                    close[num]=cym(l)+time[num];        //估价 
                    que.push(num);
                }
                swap(c[x][y],c[xx][yy]);
            }
        }
    }
    printf("%lld
",time[u]);
    return 0;
}
原文地址:https://www.cnblogs.com/yjkhhh/p/8636578.html