• 八数码总结


    题意:

    八数码问题也称为九宫问题。编号为1~8的8个正方形滑块被摆成3行3列,棋盘上还有一个空格,每次可以把与空格相邻的滑块移到空格中,而它原来的位置就成了新的空格。给定初始局面和目标局面,计算出最少的移动步数。
    参考来源:八数码的八境界

    方法1:

    暴搜+queue,如果空格用’0’表示的话可以直接将状态压缩成一个9位整数,也可以用字符串表示状态,用结构体表示状态+步数+空格的位置。

    代码:

    /*
    264137058
    815736402
    */
    #include<iostream>
    #include<map>
    #include<cstring>
    #include<queue>
    #include<cstdio>
    using namespace std;
    map<string, int>vis;
    int m, n;
    struct state{string str; int pos; int dist;};
    state s;
    string goal;
    int dx[4] = {-1, 1, 0, 0};
    int dy[4] = {0, 0, -1, 1};
    int bfs()
    {
        queue<state>q;
        q.push(s);
        vis[s.str] = 1;
        while(!q.empty()){
            state t = q.front();q.pop();
            if(t.str.compare(goal) == 0) return t.dist;
            int p= t.pos;
            int x= p / 3, y = p % 3;
            for(int i = 0; i < 4; i++){
                int newx = x + dx[i];
                int newy = y + dy[i];
                int newpos = newx * 3 + newy;
                string a = t.str;
                if(newx >= 0 && newx < 3 && newy >= 0 && newy < 3){
                    swap(a[p], a[newpos]);
                    if(vis[a]) continue;
                     vis[a] = 1;
                    q.push((state){a, newpos, t.dist + 1});
                }
            }
        }
        return -1;
    }
    int main (void)
    {
        string st;
        int pos;
        cin>>st;
        cin>>goal;
        for(int i = 0; i < 9; i++){
            if(st[i] == '0')  pos = i;
        }
        s = (state){st, pos, 0};
        int res = bfs();
        if(res == -1) cout<<"No solution"<<endl;
        else cout<<res<<endl;
    }
    
    
    

    方法2:

    模拟队列+康拓展开/哈希/STL set判重,学习紫书上的代码~
    注意其中康拓展开

    代码:

    康拓展开

    
    
    #include<iostream>
    #include<cstring>
    #include<set>
    #include<cstdio>
    using namespace std;
    typedef int state[9];//表示state 代表 int[9]
    const int maxn = 1e7 + 5;
    state st[maxn], sta, goal;
    int dist[maxn];
    int dx[4] = {-1, 1, 0, 0};
    int dy[4] = {0, 0, -1, 1};
    /*
    2 6 4 1 3 7 0 5 8
    8 1 5 7 3 6 4 0 2
    */
    /******把0~8的全排列和0~362879的整数一一对应起来****/
    int vis[400000], fact[10];
    void init()
    {
        memset(vis, 0, sizeof(vis));
        fact[0] = 1;
        for(int i = 1; i < 9; i++) fact[i] = fact[i - 1] * i;
    }
    int Insert(int s)
    {
        int code = 0;
        for(int i = 0; i < 9; i++){
            int cnt = 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;
        return vis[code] = 1;
    }
    int bfs()
    {
        init();
        int fro = 0, rear = 1;
        dist[fro] = 0;
        Insert(fro);
        while(fro < rear){
            state& s = st[fro];
            if (memcmp(goal, s, sizeof(s)) == 0) return dist[fro];
            int pos;
            for(pos = 0; pos < 9; pos++) if(!s[pos]) break;
            int x = pos / 3, y = pos % 3;
            for (int i = 0; i < 4; i++){
                int newx = x + dx[i];
                int newy = y + dy[i];
                int newpos = newx * 3 + newy;
                if (newx >= 0 && newx < 3 && newy >= 0 && newy < 3){
                    state& t = st[rear];
                    memcpy(&t, &s, sizeof(s));
                    t[newpos] = 0;
                    t[pos] = s[newpos];
                    dist[rear] = dist[fro] + 1;
                    if(Insert(rear)) rear++;
                }
            }
            fro++;
        }
        return -1;
    }
    int main (void)
    {
        for(int i = 0; i < 9; i++) cin>>st[0][i];
        for(int i = 0; i < 9; i++) cin>>goal[i];
        int res = bfs();
        if(res != -1) cout<<res<<endl;
        else cout<<"No solution"<<endl;
        return 0;
    }
    
    

    哈希:

    const int hashsize = 1000003;//大质数
    int head[hashsize], next[hashsize];
    void init()
    {
        memset(head, 0, sizeof(head));
    }
    int Hash(int s)
    {
        int tmp = 0;
        for(int i = 0; i < 9; i++)
            tmp = tmp * 10 + st[s][i];
        return tmp % hashsize;
    }
    int Insert(int s)
    {
        int h = Hash(s);
        int u = head[h];
        while(u){
            if(memcpy(st[u], st[s], sizeof(st[s])) == 0)  return 0;
            u = next[u];
        }
        next[s] = head[h];
        head[h] = s;
        return 1;
    }

    set判重:

    set<int>vis;
    void init()
    {
        vis.clear();
    }
    int 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;
    }

    方法3:

    模拟队列+双向bfs~~这里判重随便选一个就好啦~~节约一半的时间和一半的空间,真心快多了~

    代码:

    #include<iostream>
    #include<queue>
    #include<cstring>
    using namespace std;
    typedef int state[9];
    const int maxn = 1e7 + 5;
    state st[maxn];
    int dx[4] = {-1, 1, 0, 0};
    int dy[4] = {0, 0, -1, 1};
    int fact[maxn], vis[maxn], dist[3][maxn];
    /*
    2 6 4 1 3 7 0 5 8
    8 1 5 7 3 6 4 0 2
    */
    void init()
    {
        memset(vis, 0, sizeof(vis));
        fact[0] = 1;
        for(int i = 1; i < 9; i++)
            fact[i] = fact[i - 1] * i;
    }
    int canto(state s)
    {
        int code = 0;
        for(int i = 0; i < 9; i++){
            int cnt = 0;
            for(int j = i + 1; j < 9; j++){
                if(s[j] < s[i]) cnt++;
            }
            code += fact[8 - i] * cnt;
        }
        return code;
    }
    int bfs()
    {
        init();
        int cs = canto(st[0]);
        int ct = canto(st[1]);
        dist[1][cs] = 0;
        dist[2][ct] = 0;
        vis[cs] = 1;
        vis[ct] = 2;
        int fro = 0, rear  = 2;
        while(fro < rear){
            state& s = st[fro];
            cs = canto(s);
            int pos;
            for(pos = 0; pos < 9; pos++) if(!s[pos]) break;
            int x = pos / 3, y = pos % 3;
            for (int i = 0; i < 4; i++){
                int newx = x + dx[i];
                int newy = y + dy[i];
                int newpos = newx * 3 + newy;
                if (newx >= 0 && newx < 3 && newy >= 0 && newy < 3){
                    state& t = st[rear];
                    memcpy(&t, &s, sizeof(s));
                    t[pos] = s[newpos];
                    t[newpos] = s[pos];
                    ct = canto(t);
                    if(!vis[ct]){
                        vis[ct] = vis[cs];
                        dist[vis[cs]][ct] = dist[vis[cs]][cs] + 1;
                        rear++;
                    }else if(vis[ct] != vis[cs]){
                        return dist[vis[ct]][ct] + dist[vis[cs]][cs] + 1;
                    }
                }
            }
            fro++;
        }
        return -1;
    }
    int main (void)
    {
        for(int i = 0; i < 9; i++) cin>>st[0][i];
        for(int i = 0; i < 9; i++) cin>>st[1][i];
        int res = bfs();
        if(res != -1) cout<<res<<endl;
        else cout<<"No solution"<<endl;
        return 0;
    }
    

    方法4:

    A* + 优先级队列(小顶堆)+ 曼哈顿距离
    A*搜索算法

    当任何第二次走到一个点的时候,判断最小步骤是否小于记录的内容,如果是,则更新掉原最小步数,关于记录最小步数,如果使用曼哈顿距离作为h(n),则不必记录,这时的A*满足:每个点第一次被选出搜索队列时的状态为到达此点的最优解,而且每个点只会进入队列一次。所以只需要设定一个vis的数组就够了。

    代码:

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<queue>
    /*
    1 2 3 4 5 6 7 8 0
    1 2 3 4 5 6 0 7 8
    
    2 6 4 1 3 7 0 5 8
    8 1 5 7 3 6 4 0 2
    */
    using namespace std;
    struct state
    {
        int a[9];
        int pos;
        int f,g;
        int can;
         bool operator < ( const state s2)const {
            return f > s2.f;
        }
    };
    const int maxn = 1e7 + 5;
    state sta, goal;
    int dx[4] = {-1, 1, 0, 0};
    int dy[4] = {0, 0, -1, 1};
    int fact[maxn], vis[maxn];
    void init()
    {
        fact[0] = 1;
        for(int i = 1; i < 9; i++)
            fact[i] = fact[i - 1] * i;
    }
    int canto(state s)
    {
        int code = 0;
        for(int i = 0; i < 9; i++){
            int cnt = 0;
            for(int j = i + 1; j < 9; j++){
                if(s.a[j] < s.a[i]) cnt++;
            }
            code += fact[8 - i] * cnt;
        }
        return code;
    }
    int geth(state be)
    {
        int res = 0;
        for(int i = 0; i < 9; i++){
            int tmp = goal.a[i];
            for(int j = 0; j < 9; j++){
                if(be.a[j] == tmp)
                    res += abs(i/3 - j/3) + abs(i % 3 - j % 3);
                    break;
            }
        }
        return res;
    }
    int astar()
    {
        priority_queue<state> q;
        q.push(sta);
        vis[sta.can] = 1;
        while(!q.empty()){
            state s = q.top();
            q.pop();
            if(s.can == goal.can) return s.g;
            int x = s.pos / 3, y = s.pos % 3;
            for(int i = 0; i < 4; i++){
                int newx = x + dx[i];
                int newy = y + dy[i];
                int newpos = newx * 3 + newy;
                if (newx >= 0 && newx < 3 && newy >= 0 && newy < 3){
                    state t = s;
                    t.a[t.pos] = s.a[newpos];
                    t.a[newpos] = s.a[t.pos];
                    t.pos = newpos;
                    t.can = canto(t);
                    if(vis[t.can]) continue;
                    t.g = s.g + 1;
                    t.f= geth(t) + t.g;
                    q.push(t);
                    vis[t.can] = 1;
                }
            }
        }
        return -1;
    }
    void print (state t)
    {
        for(int i = 0; i < 9; i++)
            cout<<t.a[i]<<' ';
        cout<<endl;
        cout<<"pos"<<t.pos<<endl;
        cout<<"can"<<t.can<<endl;
        cout<<"f"<<t.f<<endl;
    }
    int main (void)
    {
        init();
        for(int i = 0; i < 9; i++){
            cin>>sta.a[i];
            if(sta.a[i] == 0){
                sta.pos = i;
            }
        }
        sta.g = 0;
        sta.can = canto(sta);
        //print(sta);
        for(int i = 0; i < 9; i++) cin>>goal.a[i];
        goal.can = canto(goal);
        //print(goal);
    
        int res = astar();
        if(res != -1) cout<<res<<endl;
        else cout<<"No solution"<<endl;
        return 0;
    }
    
    

    方法5:

    IDA* + 曼哈顿距离

    IDA*即迭代加深的A*搜索,实现代码是最简练的,无须状态判重,无需估价排序。那么就用不到哈希表,堆上也不必应用,空间需求变的超级少。效率上,应用了曼哈顿距离。同时可以根据深度和h值,在找最优解的时候,对超过目前最优解的地方进行剪枝,这可以导致搜索深度的急剧减少,所以,这是一个致命的剪枝!因此,IDA*大部分时候比A*还要快,可以说是A*的一个优化版本!

    代码:

    #include<iostream>
    #include<queue>
    #include<cstring>
    #include<cmath>
    using namespace std;
    const int maxn = 1e7 + 5;
    int maxdepth;
    int dx[4] = {1, 0 , 0, -1};
    int dy[4] = {0, 1, -1, 0};
    /*
    1 2 3 4 5 6 7 8 0
    1 2 3 4 5 6 0 7 8
    
    2 6 4 1 3 7 0 5 8
    8 1 5 7 3 6 4 0 2
    */
    struct state
    {
        int a[9];
        int pos;
    };
    state st[maxn];
    state goal, sta;
    int geth(state be)
    {
        int res = 0;
        for(int i = 0; i < 9; i++){
            int tmp = goal.a[i];
            if(tmp == 0) continue;
            for(int j = 0; j < 9; j++){
                if(be.a[j] == tmp){
                     res += abs(i/3 - j/3) + abs(i % 3 - j % 3);
                    break;
                }
            }
        }
        return res;
    }
    void print(state s)
    {
        for(int i = 0; i < 9; i++)
            cout<<s.a[i]<<' ';
        cout<<endl;
    }
    int dfs(state s, int h, int depth, int prei)
    {
        if(h == 0) return 1;
       // print(s);
        if(depth + h > maxdepth) return 0;
        int x = s.pos / 3, y = s.pos % 3;
        for (int i = 0; i < 4; i++){
            if(prei + i == 3) continue;
            int newx = x + dx[i];
            int newy = y + dy[i];
            int newpos = newx * 3 + newy;
            if (newx >= 0 && newx < 3 && newy >= 0 && newy < 3){
                state t = s;
                t.a[s.pos]  = s.a[newpos];
                t.a[newpos] = s.a[s.pos];
                t.pos = newpos;
    
                if(dfs(t, geth(t), depth + 1, i)) return 1;
            }
        }
        return 0;
    }
    int idastar()
    {
        int h = geth(sta);
        maxdepth = h;
        while(!dfs(sta, h, 0, -1) && maxdepth < 100) {
            maxdepth++;
        }
       return maxdepth;
    }
    int main (void)
    {
        for(int i = 0; i < 9; i++){
            cin>>sta.a[i];
            if(sta.a[i] == 0){
                sta.pos = i;
            }
        }
         for(int i = 0; i < 9; i++){
            cin>>goal.a[i];
        }
        int res = idastar();
        if(res < 100) cout<<res<<endl;
        else cout<<"No solution"<<endl;
        return 0;
    }
    

    方法6:

    有关逆序数
    空格在同一行上进行移动,1~8的排列逆序数不变,空格在同一列上进行移动,1~8的排列逆序数发生变化的对数为偶数。所以初始状态的逆序数与目标态同奇偶则有解,反之无解;
    这一优化可以用在上面的所有方法中,也是一个很重要的剪枝。

    代码:

    int Reverse(state s)
    {
        int cnt = 0;
        for(int i = 0; i < 9; i++){
            int tmp = s.a[i];
            if(tmp == 0) continue;
            for(int j = i +1; j < 9; j++){
                if(s.a[j] && s.a[i] > s.a[j]) cnt++;
            }
        }
        return cnt % 2;
    }

    真的是细节决定一切。。。竟然写了一天。。。一半时间还都用来调试。。。写的时候就应该心思细腻,检查的时候模拟一遍过程,任何细节都不能放过!

  • 相关阅读:
    wx.showToast 延时跳转~~~
    wx.request 获取不到post传递的值
    G,sql中select 如果太长,可以在后面放G,竖行显示~~~~
    用for语句从数组中剔除数据,注意,count,要放到for语句之外才行
    读代码还是读文档,来自知乎
    聊聊我对写好程序的认识
    open() 函数以 r+ 模式打开文件
    open()
    Python 流程控制:while
    Python 序列
  • 原文地址:https://www.cnblogs.com/Tuesdayzz/p/5758728.html
Copyright © 2020-2023  润新知