康托展开



前言

冒着生命危险码文……毕竟大物实验还没写完……
康托展开用来求一个数字在其全排列中排在第几位,可用做hash函数,而且这个hash函数niubility!因为可以求逆,和普通的hash函数不一样!

  1. 康托展开
    https://blog.csdn.net/lttree/article/details/24798653

Eight - POJ 1077 && HDU 1043

题目大意
八数码,以下来自 HIT 1868
在 3*3 的棋盘上有 8 个将牌,每一个将牌都刻有 1-8 数码中的某一个数码。棋盘中留有一个空格,允许其周围的某一个将牌向空格移动,这样通过移动将牌就可以不断改变将牌的布局。 给定一种初始时的将牌布局或结构(称作初始状态)和一个目标的布局(称作目标状态),问如何移动将牌,实现从初始状态到目标状态的改变,给出合法步骤

思路
本题其实是BFS,但是如何将整个数组作为状态压入队列呢?用康托展开压进去就可以了,取出来的时候再用康托展开的逆求出原数组
本题POJ和HDU是不一样的,POJ单组测试,HDU多组数据,所以能过POJ的代码不一定能过HDU
POJ中直接使用BFS,搜索过程中用pre标记是从哪个状态来的,输出的时候就递归输出就完事
HDU中因为是多组测试这样做会TLE,所以采用BFS打表,从最终状态开始BFS,直到队列为空,并用pre标记是到哪个状态去,输出的时候就直接从需求解的状态推到最终状态

POJ做法:

  #include <cstdio>
  #include <iostream>
  #include <algorithm>
  #include <cstring>
  #include <cctype>
  #include <queue>
  #include <functional>
  using namespace std;
  typedef long long ll;
  const int N = 9;
  const int M = 4e5 + 15;

  // 康托展开,从末位为第1位,首位为第n位
  // X = a[n]*(n-1)! + a[n - 1]*(n-2)! + ... + a[1]*0!

  struct node{
      int x, prex, preop;
      node(int px, int pprex, int ppop): x(px), prex(pprex), preop(ppop) {}
  };

  const int dx[] = {-1, 1, 0, 0};
  const int dy[] = {0, 0, -1, 1};
  const int fac[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320};
  const char mp[] = "udlr";
  queue<node> que;
  bool used[M];
  int  pre[M];      //记录前一个结点(其实是图论)
  int  op[M];       //记录操作

  inline void init(){
      while(!que.empty())     que.pop();
      memset(used, false, sizeof(used));
      memset(pre, -1, sizeof(pre));
      memset(op, -1, sizeof(op));
  }

  int cantor(int a[N]){
      int sum = 0;
      for(int i = 0; i < N; i++){
          int cnt = 0;
          for(int j = i + 1; j < N; j++){
              if(a[j] < a[i])     cnt++;     //后面的数比a[i]小的有几个
          }
          sum += cnt*fac[N - i - 1];        //sum += a[n] * (n - 1)!
      }
      return sum;
  }

  void inv_cantor(int num, int a[]){
      bool used[N + 1] = {0};
      for(int i = 0; i < N; i++){
          int cnt = num/fac[N - i - 1];   //比a[i]小且未用过的数有cnt个
          num %= fac[N - i - 1];

          int j;
          for(j = 0; j < N; j++){         //回推该位数字是几
              if(!used[j]){
                  if(cnt == 0)    break;
                  cnt--;
              }
          }
          a[i] = j;
          used[j] = true;
      }
  }

  void display(int x){    //递归输出
      if(pre[x] == -1)    return;
      display(pre[x]);
      cout << mp[op[x]];
  }

  void bfs(int s, int t){
      que.push(node(s, -1, -1));

      while(!que.empty()){
          node nd = que.front();
          int x = nd.x;
          que.pop();
          if(used[x] == true)     continue;
          used[x] = true;
          pre[x] = nd.prex;
          op[x] = nd.preop;

          if(x == t)              break;

          int a[N];
          inv_cantor(x, a);
          int xpos = 0;
          while(a[xpos])     xpos++;
          int i = xpos/3;         //一维映射二维
          int j = xpos%3;
          for(int dir = 0; dir < 4; dir++){
              int newi = i + dx[dir];
              int newj = j + dy[dir];
              if(newi < 0 || newi >= 3 || newj < 0 || newj >= 3)  continue;
              int newxpos = newi*3 + newj;      //二维映射一维

              swap(a[xpos], a[newxpos]);
              int newx = cantor(a);
              if(!used[newx]){
                  que.push(node(newx, x, dir));
              }
              swap(a[xpos], a[newxpos]);
          }
      }
  }

  int main(){
      ios::sync_with_stdio(false);
      while(true){
          init();
          int a[N];
          int b[N] = {1,2,3,4,5,6,7,8,0};
          char ch;
          for(int i = 0; i < N; i++){
              cin >> ch;
              if(cin.eof())                   break;
              if('1' <= ch && ch <= '8')      a[i] = ch - '0';
              else                            a[i] = 0;
          }
          if(cin.eof())                       break;

          int s = cantor(a);
          int t = cantor(b);
          bfs(s, t);

          if(s == t){
              cout << "lr" << endl;
          }else if(pre[t] == -1){
              cout << "unsolvable" << endl;
          }else{
              display(t);
              cout << endl;
          }
      }
  }


HDU做法:

  #include <cstdio>
  #include <iostream>
  #include <algorithm>
  #include <cstring>
  #include <cctype>
  #include <queue>
  #include <functional>
  using namespace std;
  typedef long long ll;
  const int N = 9;
  const int M = 4e5 + 15;

  // 康托展开,从末位为第1位,首位为第n位
  // X = a[n]*(n-1)! + a[n - 1]*(n-2)! + ... + a[1]*0!

  struct node{
      int x, prex, preop;
      node(int px, int pprex, int ppop): x(px), prex(pprex), preop(ppop) {}
  };

  const int dx[] = {-1, 1, 0, 0};
  const int dy[] = {0, 0, -1, 1};
  const int fac[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320};
  const char mp[] = "durl";
  queue<node> que;
  bool used[M];
  int  pre[M];
  int  op[M];

  inline void init(){
      while(!que.empty())     que.pop();
      memset(used, false, sizeof(used));
      memset(pre, -1, sizeof(pre));
      memset(op, -1, sizeof(op));
  }

  int cantor(int a[N]){
      int sum = 0;
      for(int i = 0; i < N; i++){
          int cnt = 0;
          for(int j = i + 1; j < N; j++){
              if(a[j] < a[i])     cnt++;     //后面的数比a[i]小的有几个
          }
          sum += cnt*fac[N - i - 1];
      }
      return sum;
  }

  void inv_cantor(int num, int a[]){
      bool used[N + 1] = {0};
      for(int i = 0; i < N; i++){
          int cnt = num/fac[N - i - 1];
          num %= fac[N - i - 1];

          int j;
          for(j = 0; j < N; j++){
              if(!used[j]){
                  if(cnt == 0)    break;
                  cnt--;
              }
          }
          a[i] = j;
          used[j] = true;
      }
  }

  void display(int x){
      while(true){
          if(x == -1)     return;
          cout << mp[op[x]];
          x = pre[x];
      }
  }

  void bfs(int s){
      que.push(node(s, -1, -1));

      while(!que.empty()){
          node nd = que.front();
          int x = nd.x;
          que.pop();
          if(used[x] == true)     continue;
          used[x] = true;
          pre[x] = nd.prex;
          op[x] = nd.preop;

          int a[N];
          inv_cantor(x, a);
          int xpos = 0;
          while(a[xpos])     xpos++;
          int i = xpos/3;
          int j = xpos%3;
          for(int dir = 0; dir < 4; dir++){
              int newi = i + dx[dir];
              int newj = j + dy[dir];
              if(newi < 0 || newi >= 3 || newj < 0 || newj >= 3)  continue;
              int newxpos = newi*3 + newj;

              swap(a[xpos], a[newxpos]);
              int newx = cantor(a);
              if(!used[newx]){
                  que.push(node(newx, x, dir));
              }
              swap(a[xpos], a[newxpos]);
          }
      }
  }

  int main(){
      ios::sync_with_stdio(false);
      init();
      int b[N] = {1,2,3,4,5,6,7,8,0};
      int t = cantor(b);
      bfs(t);
      while(true){
          int a[N];
          char ch;
          for(int i = 0; i < N; i++){
              cin >> ch;
              if(cin.eof())                   break;
              if('1' <= ch && ch <= '8')      a[i] = ch - '0';
              else                            a[i] = 0;
          }
          if(cin.eof())                       break;

          int s = cantor(a);
          if(s == t){
              cout << "lr" << endl;
          }else if(pre[s] == -1){
              cout << "unsolvable" << endl;
          }else{
              display(s);
              cout << endl;
          }
      }
  }


魔板 - HDU 1430

题目大意
(略)

思路
同样是康托展开,只是这题容易WA,QAQ
这题对于一开始的输入需要预处理,才能够始终对应BFS打的表,将初态映射为12345678,目态则一一对应映射
注意到题目要求字典序最小,所以搜索顺序就需要保持ABC(开优先队列反而WA了,后来想想用普通队列能够维持字典序最小,优先队列采用策略错误反而不能)

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
const int N = 8;
const int M = 4e5 + 15;

const int fac[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320};
const char convert_op[] = "ABC";

struct node{
    int val, op, pre;
    node(int pval, int ppop, int ppre): val(pval), op(ppop), pre(ppre) {}
};
queue<node> que;
int op[M], pre[M];
bool used[M];

inline void init(){
    while(!que.empty())     que.pop();
    memset(op, -1, sizeof(op));
    memset(pre, -1, sizeof(pre));
    memset(used, false, sizeof(false));
}

int cantor(char a[]){
    int sum = 0;
    for(int i = 0; i < N; i++){
        int cnt = 0;
        for(int j = i + 1; j < N; j++){
            if(a[j] < a[i])     cnt++;
        }
        sum += cnt*fac[N - i - 1];
    }
    return sum;
}

void inv_cantor(int num, char a[]){
    bool used[N + 1] = {false};
    for(int i = 0; i < N; i++){
        int k = num/fac[N - i - 1];
        num %= fac[N - i - 1];

        int j;
        for(j = 1; j <= N; j++){
            if(!used[j]){
                if(k == 0)  break;
                k--;
            }
        }
        a[i] = j + '0';
        used[j] = true;
    }
}

void bfs(char s[]){
    int sval = cantor(s);
    que.push(node(sval, -1, -1));

    while(!que.empty()){
        node nd = que.front();
        que.pop();
        int val = nd.val;
        if(used[val])   continue;
        used[val] = true;
        pre[val] = nd.pre;
        op[val] = nd.op;

        char a[N + 1] = {0};
        inv_cantor(val, a);

        //A:上下交换
        for(int i = 0; i < 4; i++){ swap(a[i], a[N - i - 1]);}
        int con_a_val = cantor(a);
        if(!used[con_a_val])    que.push(node(con_a_val, 0, val));
        for(int i = 0; i < 4; i++){ swap(a[i], a[N - i - 1]);}

        //B:循环右移
        for(int i = 2; i >= 0; i--)     { swap(a[i], a[i + 1]);}
        for(int i = 4; i <= N - 2; i++) { swap(a[i], a[i + 1]);}
        int con_b_val = cantor(a);
        if(!used[con_b_val])    que.push(node(con_b_val, 1, val));
        for(int i = 0; i <= 2; i++)     { swap(a[i], a[i + 1]);}
        for(int i = N - 2; i >= 4; i--) { swap(a[i], a[i + 1]);}

        //C:中心四格顺时针旋转
        swap(a[5], a[6]);
        swap(a[2], a[5]);
        swap(a[1], a[2]);
        int con_c_val = cantor(a);
        if(!used[con_c_val])    que.push(node(con_c_val, 2, val));
    }
}

void display(int val){
    if(op[val] == -1)   return;
    display(pre[val]);
    char a[10] = {0};
    inv_cantor(val, a);
    printf("%c", convert_op[op[val]]);
}

int main()
{
    init();
    char init_seq[] = "12345678";
    bfs(init_seq);

    char s[N + 1], t[N + 1];
    while(~scanf("%s%s", s, t)){
        //预处理完成映射
        char mp[N + 1];
        for(int i = 0; i < 8; i++){
            mp[s[i] - '0'] = i + '0' + 1;
        }
        for(int i = 0; i < 8; i++){
            t[i] = mp[t[i] - '0'];
        }

        int val = cantor(t);
        display(val);
        puts("");
    }
}