Eight HDU - 1043 八数码问题 康托展开 + 反向 bfs +记录路径

图

bfs 剪枝要点

  1. visit数组 hash函数(康托展开)
  2. 记忆化 bfs 打表存储所有可达路径
  3. 优先队列 periority queue
  4. 多点同时bfs
  5. 反向bfs + bfs 打表存储所有路径

stl + 正向bfs

9! 一共有 362880 种可能,10e5 数据规模并不是非常大,所以我考虑用 map<string , bool> 来作为visit。map 的访问和插入都是 log n
所以对于每一个输入的序列,时间复杂度都是 n*log n
log 362880 约等于 20 ,所以总的时间复杂度等于 36288020 = 7257600 , 71e6,对单个testcase 是可接受的。

如果再考虑 有多个 test case13个testcase 就会达到 1e8 的时间复杂度,显然是不可接受的。

正向bfs 加 hash判重

此处要引入一个定理 康托展开和逆康托展开

康托展开是一个全排列到一个自然数的双射,常用于构建hash表时的空间压缩。设有n个数(1,2,3,4,…,n),可以有组成不同(n!种)的排列组合,康托展开表示的就是是当前排列组合在n个不同元素的全排列中的名次。 (转自此处)

显然如果我们此处使用康托展开作 hash 映射,visit 数组就可以从 一个 map 退化成一个 一维数组
时间复杂度是 n 同上

但是这么写的话,会有很多次重复的操作。所以testcase过大,也会 t。

反向bfs + 记忆化 + hash 判重

由于树的结构特点,一个子节点一定只有一个父节点。
所以可以直接从根节点开始便利,使用链式前向星的思想,对每个节点,记录该节点的父节点。

然后只需要进行一次bfs 搜索即可。
代码如下:

#include<iostream>
#include<queue>
#include<cstring>
#include<string>
#include<vector>

using namespace std;
struct node
{
    int pre;
    int op;
    string s;
    int x;
    int y;
};
node path[400000];
int vis[400000];

int dir[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
char ss[4] = { 'u','d','l','r'};

int  fac[] = {1,1,2,6,24,120,720,5040,40320,362880}; //i的阶乘为fac[i]
int Cantor(string s){
    int sum = 0;
    for(int i = 0; i < 9; i++){
        int num = 0;
        for(int j = i+1; j < 9; j++){
            if(s[j] < s[i])
                num++;//确定当前元素i在未出现的元素中是第几个(从小到大)
        }
        sum += fac[8-i] * num;
    }
    return sum;
}

void bfs()
{
    int counts =0;
    path[counts].pre = -1;
    path[counts].s = "123456780";
    path[counts].x=2;
    path[counts].y=2;
    queue<int> qu;
    qu.push(counts);
    int visn;

    visn = Cantor(path[counts].s);
    vis[visn] =counts;
    counts++;

    while(!qu.empty())
    {
        int f = qu.front();
        qu.pop();

        int x = path[f].x;
        int y = path[f].y;


        char tp;
        for(int i=0;i<4;i++)
        {
            int px = x+dir[i][0];
            int py = y+dir[i][1];
            if(px<0||px>2||py<0||py>2)continue;

             tp = path[f].s[px*3+py];  // 开始转换
             path[f].s[px*3+py] = path[f].s[x*3+y];
             path[f].s[x*3+y] = tp;


             visn = Cantor(path[f].s);  //判断是否出现过
             if(vis[visn]!=-1)
             {
                 tp = path[f].s[px*3+py];  //  回溯
                 path[f].s[px*3+py] = path[f].s[x*3+y];
                 path[f].s[x*3+y] = tp;
                 continue;
             }
             vis[visn] =counts;

             path[counts].op = i;
             path[counts].pre = f;
             path[counts].s = path[f].s;
             path[counts].x = px;
             path[counts].y = py;
             qu.push(counts);
             counts++;




             tp = path[f].s[px*3+py];    //回溯
             path[f].s[px*3+py] = path[f].s[x*3+y];
             path[f].s[x*3+y] = tp;
        }

    }

}

int main()
{
    for(int i=0;i<400000;i++)vis[i]=-1;
    bfs();

    char st1[30];
    string st;
    ios::sync_with_stdio(false);
    vector<int> re;

    while(cin.getline(st1,25))
    {

        st="";
        re.clear();
        for(int i=0;i<17;i+=2)
        {
            if(st1[i]=='x')st = st+'0';
            else st= st+st1[i];
        }
        int viss;
        viss = Cantor(st);
    
        if(vis[viss]!=-1)
        {
             int flag=vis[viss];
             while(path[flag].pre!=-1)
             {
                 cout<<ss[path[flag].op];
                 flag = path[flag].pre;
             }cout<<endl;
        }
         else cout<<"unsolvable"<<endl;

    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/jackcily/article/details/83212557
今日推荐