八数码路径寻找算法

1.        使用全排列的编码/解码(最高效)

代码:

#include<cstdio>

#include<cstring>

using namespace std;

 

typedef int State[9]; //State相当于 int [9]

const int MAXSTATE = 1000000;// 最多的状态数目

State st[MAXSTATE], goal; //st相当于intst[MAXSTATE][9];goal 相当于 int goal[9];

int dist[MAXSTATE];//存储距离

int vis[362880], fact[9];

// 9!=362880,vis中的下标是解码后的大小,fact中数组元素的值是编码的大小

 

void init_lookup_table() //编码函数:使得fact[n]=n!

{

    fact[0] =1;

    for(int i= 1; i < 9; i++)

    {

       fact[i] = fact[i-1] * i;

    }

}

 

int try_to_insert(int s)  //解码函数:将每一种状态对应在数组st 中的下标s映射到整数code

{

    int code= 0;

    for(int i= 0; i < 9; i++)

    {

        intcnt = 0;

       for(int j = i+1; j < 9; j++)

        {

           if(st[s][j] < st[s][i])

            {

               cnt++;

            }

        }

        code+= fact[8-i] * cnt;

    }

   if(vis[code]) //如果该状态已经被访问过,则不能再次访问,没有成功插入查找表

    {

       return 0;

    }

    returnvis[code] = 1; //标记已访问

}

 

const int dx[] = {-1, 1, 0, 0};// 四个方向

const int dy[] = {0, 0, -1, 1};

 

int bfs()

{

   init_lookup_table(); //初始化查找表

    int front= 1, rear = 2; //不使用下标0,因为0被视为“不存在”

   while(front < rear)

    {

       State& s = st[front]; //使用引用简化代码

        if(memcmp(goal,s, sizeof(s)) == 0)// 如果到达目标状态

        {

            return front;//返回目标状态的下标

        }

        intz;//标记数组中0的位置

        for(z= 0; z < 9; z++)

        {

           if(!s[z]) break;

        }

        int x= z/3, y = z%3; //x:是0所在的行数;y是0所在的列数,两者的范围均为0—2

       for(int d = 0; d < 4; d++) //依次向0周围的四个方向走去

        {

           int newx = x + dx[d];//0的新行数

           int newy = y + dy[d];//0的新列数

           int newz = newx * 3 + newy;//0的新下标

           if(newx >= 0 && newx < 3 && newy >= 0&& newy < 3) //新位置没有越界

            {

               State& t = st[rear]; //下一个状态

               memcpy(&t, &s, sizeof(s));//首先将上一个状态复制到新的状态中

               t[newz] = s[z];//然后修改变化的值:0与其中一个值互换位置

               t[z] = s[newz];

               dist[rear] = dist[front] + 1;// 下一个状态距离初始状态的距离+1

               if(try_to_insert(rear))

/*

成功插入查找表(该状态从未出现),修改队尾的指针。如果没有执行rear++语句,表明当前的状态st[rear]已经存在,现在又生成了一个相同的状态,所以状态出现重复,下次循环的时候去掉重复的状态,改为新状态下的后续状态。

*/

               {

                   rear++;

               }

            }

        }

       front++;

    }

    return 0;

}

 

int main()

{

    for(int i= 0; i < 9; i++)

    {

       scanf("%d", &st[1][i]); //读入初始状态

    }

    for(int i= 0; i < 9; i++)

    {

       scanf("%d", &goal[i]); //读入目标状态

    }

    int ans =bfs();

    if(ans> 0)

    {

       printf("%d\n", dist[ans]);

    }

    else

    {

        printf("-1\n");

    }

    return 0;

}

示例输入:

2 6 4 1 3 7 0 5 8

8 1 5 7 3 6 4 0 2

示例输出:

31

2.        使用哈希表(竞赛中最常用)

#include<cstdio>

#include<cstring>

#include<set>

using namespace std;

 

typedef int State[9];

const int MAXSTATE = 1000000;

State st[MAXSTATE], goal;

int dist[MAXSTATE];

const int MAXHASHSIZE =1000003;

int head[MAXHASHSIZE],Next[MAXSTATE];

 

void init_lookup_table()

{

    memset(head, 0, sizeof(head));

}

 

int hash(State& s)

{

    int v = 0;

    for(int i = 0; i < 9; i++)

    {

        v = v * 10 + s[i];  将排列转换成九位的正整数

    }

    return v % MAXHASHSIZE; 确保hash函数值是不超过hash表的大小的非负整数

}

 

int try_to_insert(int s)

{

    int h = hash(st[s]);

    int u = head[h]; 从表头开始查找链表

    while(u)

    {

        if(memcmp(st[u], st[s], sizeof(st[s]))== 0)  找到了插入失败

        {

            return 0;

        }

        u = Next[u];顺着链表,继续找

    }

    Next[s] = head[h]; 插入到链表中

    head[h] = s;

    return 1;

}

 

const int dx[] = {-1, 1, 0,0};

const int dy[] = {0, 0, -1,1};

 

int bfs()

{

    init_lookup_table();

    int front = 1, rear = 2;

    while(front < rear)

    {

        State& s = st[front];

        if(memcmp(goal, s, sizeof(s)) == 0)

        {

            return front;

        }

        int z;

        for(z = 0; z < 9; z++)

        {

            if(!s[z]) break;

        }

        int x = z/3, y = z%3;

        for(int d = 0; d < 4; d++)

        {

            int newx = x + dx[d];

            int newy = y + dy[d];

            int newz = newx * 3 + newy;

            if(newx >= 0 && newx< 3 && newy >= 0 && newy < 3)

            {

                State& t = st[rear];

                memcpy(&t, &s,sizeof(s));

                t[newz] = s[z];

                t[z] = s[newz];

                dist[rear] = dist[front] + 1;

                if(try_to_insert(rear))

                {

                    rear++;

                }

            }

        }

        front++;

    }

    return 0;

}

 

int main()

{

    for(int i = 0; i < 9; i++)

    {

        scanf("%d", &st[1][i]);

    }

    for(int i = 0; i < 9; i++)

    {

        scanf("%d", &goal[i]);

    }

    int ans = bfs();

    if(ans > 0)

    {

        printf("%d\n", dist[ans]);

    }

    else

    {

        printf("-1\n");

    }

    return 0;

}

3.        STL最好写,但是效率最低。使用STL集合,将每一个状态转换成9位的10进制整数,就可以用set<int>判重

#include<cstdio>

#include<cstring>

#include<set>

using namespace std;

 

typedef int State[9];

const int MAXSTATE = 1000000;

State st[MAXSTATE], goal;

int dist[MAXSTATE];

set<int> vis;

 

void init_lookup_table()

{

   vis.clear();

}

 

int try_to_insert(int s)

{

   int v = 0;

   for(int i = 0; i < 9; i++)

    {

       v = v * 10 + st[s][i];

    }

   if(vis.count(v))

    {

       return 0;

    }

   vis.insert(v);

   return 1;

}

 

const int dx[] = {-1, 1, 0, 0};

const int dy[] = {0, 0, -1, 1};

 

int bfs()

{

   init_lookup_table();

   int front = 1, rear = 2;

   while(front < rear)

    {

       State& s = st[front];

       if(memcmp(goal, s, sizeof(s)) == 0)

       {

           return front;

       }

       int z;

       for(z = 0; z < 9; z++)

       {

           if(!s[z])

           {

                break;

           }

       }

       int x = z/3, y = z%3;

       for(int d = 0; d < 4; d++)

       {

           int newx = x + dx[d];

           int newy = y + dy[d];

           int newz = newx * 3 + newy;

           if(newx >= 0 && newx < 3 && newy >= 0&& newy < 3)

           {

                State& t = st[rear];

                memcpy(&t, &s,sizeof(s));

                t[newz] = s[z];

                t[z] = s[newz];

                dist[rear] = dist[front] + 1;

                if(try_to_insert(rear))

                {

                    rear++;

                }

           }

       }

       front++;

    }

   return 0;

}

 

int main()

{

   for(int i = 0; i < 9; i++)

    {

       scanf("%d", &st[1][i]);

    }

    for(inti = 0; i < 9; i++)

    {

       scanf("%d", &goal[i]);

    }

   int ans = bfs();

   if(ans > 0)

    {

       printf("%d\n", dist[ans]);

    }

   else

    {

       printf("-1\n");

    }

   return 0;

}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值