zoukankan      html  css  js  c++  java
  • 【双向BFS+康托展开】Eight Ⅱ HDU

    HDU - 3567 Eight II

    题意:八数码问题,给定初始状态与目标状态,输出字典序最小的移动路径和步数。

    思路:

    • 双向BFS:

      以初始状态和目标状态为两个起点,同时出发,汇合时即答案

      但需要注意,从初始状态出发的正向BFS所得路径必是字典序最小的(前提是方向遍历是按字典序从小到大);从目标状态出发的反向BFS则不一定,当反向BFS遇到之前搜索过的反向状态时,需要比较新的路径字典序和已有的路径字典序的大小。

      以及需要把棋盘信息存进结构体里面,以省去逆康托展开的步骤,否则会TLE

    #include<iostream>
    #include<stdlib.h>
    #include<time.h>
    #include<algorithm>
    #include<vector>
    #include<set>
    #include<queue>
    #include<cstring>
    #include<cstdio>
    #include <string>
    /*#define mem(a,b) memset(a,b,sizeof(a))
    #define f(i,a,b) for(int i=a;i<=b;i++)
    #define af(i,a,b) for(int i=a;i>=b;i--)
    */
    
    using namespace std;
    
    typedef long long LL;
    const LL INF = 0x3f3f3f3f;
    const int maxn = 362885;
    
    const int FAC[] = { 1,1,2,6,24,120,720,5040,40320,362880,3628800 };
    
    int cantor(int* a) {//算出全排列对应的哈希值
        int x = 0;
        for (int i = 0; i < 9; i++) {
            int smaller = 0;
            for (int j = i + 1; j < 9; j++) {
                if (a[j] < a[i]) smaller++;
            }
            x += FAC[9 - i - 1] * smaller;
        }
        return x+1;
        //注意全排列数组a是从零开始的
    }
    
    /*
    void decantor(int x,int*ans) {//x哈希值,n数字个数,a算出的全排列
        x--;
        vector<int> v;
        for (int i = 1; i <= 9; i++) v.push_back(i);
        for (int i = 0; i < 9; i++) {
            int r;
            r = x / FAC[9 - i - 1];
            x = x % FAC[9 - i - 1];
            sort(v.begin(), v.end());
            ans[i] = v[r];
            v.erase(v.begin() + r);
        }
        //注意算出的全排列数组ans是从0开始的
    }
    */
    
    //描述每个状态需要有以下信息
    struct node {
        int a[15];
        int hash;
        int pos;
        int deep;
    };
    
    string path[maxn];
    int vis_go[maxn];
    int vis_back[maxn];
    int dx[] = { 1,0,0,-1 };
    int dy[] = { 0,-1,1,0 };
    char go_op[] = "dlru";
    //正向的记录
    //要求字典序最小,所以在尝试的时候也从小到大尝试
    char back_op[] = "urld";
    //反向的记录
    
    queue<node> q_go;//正向的队列
    queue<node> q_back;//反向的队列
    
    void bfs(node begin,node end) {
        if (begin.hash == end.hash) {
            cout << 0 << endl << endl;
            return;
        }
        q_go.push(begin);
        vis_go[begin.hash] = 1;
        path[begin.hash] = "";
        q_back.push(end);
        vis_back[end.hash] = 1;
        path[end.hash] = "";
    
        int L = 0;
        while (!q_go.empty() || !q_back.empty()) {
            while (!q_go.empty() && q_go.front().deep == L) {
                node tmp = q_go.front();
                q_go.pop();
                for (int i = 0; i < 4; i++) {
                    int nx = tmp.pos / 3 + dx[i];
                    int ny = tmp.pos % 3 + dy[i];
                    int npos = nx * 3 + ny;
                    if (nx < 0 || ny < 0 || nx>2 || ny>2) continue;
                    node now=tmp;
                    swap(now.a[tmp.pos], now.a[npos]);
                    now.hash = cantor(now.a);
                    now.pos = npos;
                    now.deep = tmp.deep + 1;
                    if (vis_back[now.hash]) {
                        //正向,进行本次操作之后变成了反向搜索过的状态
                        //此时正反汇合了,路径已贯通
                        cout << (int)path[now.hash].size() + (int)path[tmp.hash].size() + 1 << endl;
                        //+1是本次操作的一次
                        reverse(path[now.hash].begin(), path[now.hash].end());
                        //此时path[now.hash]储存的是反向搜索的路径,但由于记录是正向的,这里要反转字符串
                        cout << path[tmp.hash] << go_op[i] << path[now.hash] << endl;
                        //先输出正向搜索的路径,以及本次的操作,然后再输出反向搜索的路径
                        return;
                    }
                    if (vis_go[now.hash]) continue;
                    //正向,进行本次操作后变成了正向搜索过的状态,就跳过
                    q_go.push(now);
                    vis_go[now.hash] = 1;
                    path[now.hash] = path[tmp.hash] + go_op[i];
    
                    //cout << "go:"<<path[now.hash] << endl;
                }
            }
    
            while (!q_back.empty() && q_back.front().deep == L) {
                node tmp = q_back.front();
                q_back.pop();
                for (int i = 0; i < 4; i++) {
                    int nx = tmp.pos / 3 + dx[i];
                    int ny = tmp.pos % 3 + dy[i];
                    int npos = nx * 3 + ny;
                    if (nx < 0 || ny < 0 || nx>2 || ny>2) continue;
                    node now=tmp;
                    swap(now.a[tmp.pos], now.a[npos]);
                    now.hash = cantor(now.a);
                    now.pos = npos;
                    now.deep = tmp.deep + 1;
    
                    if (vis_go[now.hash]) continue;
                    //反向,进行本次操作后遇到了正向搜索过的状态,跳过
                    if (vis_back[now.hash] && path[now.hash].size() && path[now.hash][(int)path[now.hash].size() - 1] < back_op[i]) continue;
                    //如果遇到了反向搜索过的状态
                    //比较已有路径的最后一个字母和本次操作的字母大小
                    //如果本次操作字母更大,说明替换后新的路径字典序更大,不能替换
                    //否则用新的路径替换掉原有路径
    
                    q_back.push(now);
                    vis_back[now.hash] = 1;
                    path[now.hash] = path[tmp.hash] + back_op[i];
    
                    //cout << "back:" << path[now.hash] << endl;
                
                }
            }
    
            L++;
            //两个队列进行下一层搜索
        }
    }
    
    void clear() {
        while (!q_go.empty()) q_go.pop();
        while (!q_back.empty()) q_back.pop();
        memset(vis_go, 0, sizeof(vis_go));
        memset(vis_back, 0, sizeof(vis_back));
    }
    
    
    int main()
    {
        ios::sync_with_stdio(false);
        int t; cin >> t; getchar();// while (t--) {
        for (int k = 1; k <= t; k++) {
            clear();
            string s;
            node temp[3];
    
            for (int i = 1; i <= 2; i++) {
                //初始状态和目标状态入队,双向BFS
                getline(cin, s);
                int j = 0;
                for (int p = 0; p < s.size(); p++) {
                    if (s[p] == 'X') {
                        temp[i].a[j++] = 9;
                        temp[i].pos = j - 1;
                    }
                    else if (isdigit(s[p])) temp[i].a[j++] = s[p] - '0';
                }
                temp[i].hash = cantor(temp[i].a);
                temp[i].deep = 0;
            }
            
            cout << "Case " << k << ": ";
            bfs(temp[1], temp[2]);
    
        }
        return 0;
    }
    }
    
  • 相关阅读:
    【CSP2019模拟】题解
    【Codeforces 868 G】— El Toll Caves(类欧几里得)
    【Codeforces 868 G】— El Toll Caves(类欧几里得)
    如何写出规范的代码? 做一名追求极致的软件工程师!
    浏览器原理
    URL(待整合到HTTP书中哦)
    FTP服务器
    background-image 和 img
    XML的总结学习
    逻辑思维 代码逻辑
  • 原文地址:https://www.cnblogs.com/streamazure/p/13372335.html
Copyright © 2011-2022 走看看