宽搜经典题之二——8数码难题+康托展开

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了宽搜经典题之二——8数码难题+康托展开相关的知识,希望对你有一定的参考价值。

宽搜的定义在上次宽搜一中已讲,现在直接看跟本题有关的”康托展开“。

什么是”康托展开“?其实就是宽搜中实现其主要思想的一个工具——已经考察过的状态就不再考察。

解释:X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! ,其中a[i]为当前未出现的元素中是排在第几个(从0开始)。这就是康托展开。康托展开可用代码实现。

康托展开就是一种特殊的哈希函数。

  把一个整数X展开成如下形式:

  X=a[n]*n!+a[n-1]*(n-1)!+...+a[2]*2!+a[1]*1!

  其中,a为整数,并且0<=a<i,i=1,2,..,n

  {1,2,3,4,...,n}表示1,2,3,...,n的排列如 {1,2,3} 按从小到大排列一共6个。123 132 213 231 312 321 。

  代表的数字 1 2 3 4 5 6 也就是把10进制数与一个排列对应起来。

  他们间的对应关系可由康托展开来找到。

  如我想知道321是{1,2,3}中第几个大的数可以这样考虑 :

  第一位是3,当第一位的数小于3时,那排列数小于321 如 123、 213 ,小于3的数有1、2 。所以有2*2!个。再看小于第二位2的:小于2的数只有一个就是1 ,所以有1*1!=1 所以小于321的{1,2,3}排列数有2*2!+1*1!=5个 
。所以321是第6个大的数。 2*2!+1*1!是康托展开。

  再举个例子:1324是{1,2,3,4}排列数中第几个大的数:第一位是1小于1的数没有,是0个 0*3! 第二位是3小于3的数有1和2,但1已经在第一位了,所以只有一个数2 1*2! 。第三位是2小于2的数是1,但1在第一位,所以 
有0个数 0*1! ,所以比1324小的排列有0*3!+1*2!+0*1!=2个,1324是第三个大数。

 

是caioj上的题目。
 

1046: [视频]宽搜1(8数码问题)

时间限制: 1 Sec  内存限制: 128 MB
提交: 362  解决: 140
[提交][状态][讨论版]

题目描述

【题目描述】

技术分享

初始状态的步数就算1
【输入格式】
第一个3*3的矩阵是原始状态;
第二个3*3的矩阵是目标状态。
【输出格式】
输出移动所用最少的步数。
【样例1输入】
2 8 3
1 6 4
7 0 5
1 2 3
8 0 4
7 6 5
【样例1输出】
6
 
【样例2输入】
2 8 3
1 6 4
7 0 5
0 1 2
3 4 5
8 7 6
【样例2输出】
18

上代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
const int fac[10]={1,1,2,6,24,120,720,5040,40320,362880};
const int dx[]={-1, 1, 0, 0};
const int dy[]={ 0, 0,-1, 1};
struct node{
    int a[3][3],x0,y0;
    int step;
    node():step(0){ }
};
node b,e;
queue<node> q;
bool f[400000];
  
void find0(node &x){
    for (int i=0;i<3;i++)
        for (int j=0;j<3;j++)
            if (x.a[i][j]==9){
                x.x0 = i;
                x.y0 = j;
                return;
            }
}
void init(){
    for (int i=0;i<3;i++)
        for (int j=0;j<3;j++){
            cin>>b.a[i][j];
            if (b.a[i][j]==0) b.a[i][j]=9;
        }           
    find0(b); b.step=1;
    for (int i=0;i<3;i++)
        for (int j=0;j<3;j++){
            cin>>e.a[i][j];
            if (e.a[i][j]==0) e.a[i][j]=9;
        }           
    find0(e);
    memset(f,0,sizeof(f));
}
int cantor(const node &x){
    int t=0,d=0;
    for (int i=0;i<3;i++)
        for (int j=0;j<3;j++){
            d = 0; int k,p;
            for (k=0;k<3;k++)
                for (p=0;p<3;p++)
                    if (k*3+p>i*3+j && x.a[i][j]>x.a[k][p])
                        d++;
            t += d*fac[8-(3*i+j)];               
        }
    return t+1;
}
bool issame(const node &x,const node &y){
  for (int i=0;i<3;i++)
      for (int j=0;j<3;j++)
          if (x.a[i][j]!=y.a[i][j])
              return false;
  return true;
}
  
int bfs(const node &b){
    q.push(b);
    f[cantor(b)] = true;
    node u,v;   
    while (!q.empty()){
        u = q.front();
        if (issame(u,e)){//(cantor(u)==cantor(e)){
            return u.step ;
        }
        q.pop();
        for (int i=0;i<4;i++){
            v = u;
            v.x0 += dx[i];
            v.y0 += dy[i];
            if (v.x0<0 || v.x0>=3 || v.y0<0 || v.y0>=3) continue;
            swap(v.a[v.x0][v.y0],v.a[u.x0][u.y0]);
            if (f[cantor(v)]) continue;
            v.step++;
            q.push(v);
            f[cantor(v)] = true;           
        }
    }
    return -1;
}
int main(){
    init();
    cout<<bfs(b)<<endl;
    return 0;
}
即将更新:宽搜经典题之三——魔板+康托展开(为什么宽搜又带康托展开?它的精髓在哪?且听下回分解^_^)




























以上是关于宽搜经典题之二——8数码难题+康托展开的主要内容,如果未能解决你的问题,请参考以下文章

八数码难题

双向广搜+hash+康托展开 codevs 1225 八数码难题

BFS+康托展开(洛谷1379 八数码难题)

康托展开与八数码问题

HDU - 1043Eight(反向bfs+康托展开)

Luogu2578 [ZJOI2005]九数码游戏