1. 程式人生 > >寬搜經典題之二——8數碼難題+康托展開

寬搜經典題之二——8數碼難題+康托展開

col out number ini 它的 proc == pty font

寬搜的定義在上次寬搜一中已講,現在直接看跟本題有關的”康托展開“。

什麽是”康托展開“?其實就是寬搜中實現其主要思想的一個工具——已經考察過的狀態就不再考察。

解釋: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數碼難題+康托展開