转自http://www.mamicode.com/info-detail-1534200.html
康托展开
X = a[1]*(n-1)!+a[2]*(n-2)!+...+a[i]*(n-i)!+...+a[n-1]*1!+a[n]*0!
其中a[i]表示在num[i+1..n]中比num[i]小的数的数量
逆康托展开
由于:
a[i]≤n-i, a[i]*(n-i)!≤(n-i)*(n-i)!<(n-i+1)!
于是我们得到:
对于X=a[1]*(n-1)!+a[2]*(n-2)!+...+a[i]*(n-i)!+...+a[n-1]*1!+a[n]*0!
中的a[i]来说,无论a[i+1..n]的值为多少,其后面的和都不会超过(n-i)!
eg:当i=1 所以X约等于a[1]*(n-1)! +后面
X除以(n-1)!,得到商c和余数r。其中c就等于a[1],r则等于后面的部分
这样依次求解,就可以得到a[]数组了!
比如求解3的全排列中,编号为3的排列:
3 / 2! = 1 ... 1 => a[1] = 1
1 / 1! = 1 ... 0 => a[2] = 1
0 / 0! = 0 ... 0 => a[3] = 0
之后我们回想一下a[i]和含义
a[i]表示在num[i+1..n]中比num[i]小的数的数量
对于已经排好序的序列比如
{1, 2, 3},
a[] = {1, 1, 0}
unused = {1, 2, 3}, a[1] = 1, num[1] = 2
unused = {1, 3}, a[2] = 1, num[2] = 3
unused = {1}, a[3] = 0, num[3] = 1
=> 2, 3, 1
解释看http://www.mamicode.com/info-detail-1534200.html
讲得很好
代码也很易懂
#include <algorithm>
#include <cstring>
#include <string.h>
#include <iostream>
#include <list>
#include <map>
#include <set>
#include <stack>
#include <string>
#include <utility>
#include <queue>
#include <vector>
#include <cstdio>
#include <cmath>#define LL long long
#define N 100005
#define INF 0x3ffffffusing namespace std;int factor[]={1,1,2,6,24,120,720,5040,40320};
vector<int>stnum; //八数码某个状态序列
int destination; //目标状态康托展开值struct state
{int f; //评估值和状态值的总和。int g; //从起始状态到当前状态的最少步数int h; //评估函数int k; //该状态康托展开值state(int f,int g,int h,int k):f(f),g(g),h(h),k(k){};friend bool operator<(state a,state b){if(a.f!=b.f)return a.f>b.f;else return a.g>b.g;}
};int cantor(vector<int>num) //康托展开
{int k = 0;int n = 9;for(int i=0;i<n;i++) {int tp = 0;for(int j = i+1; j < n; j++) {if(num[j] < num[i]) {tp++;}}k += tp * factor[n-1-i];}return k;
}vector<int> recantor(int k) //逆康托展开
{vector<int>num;int a[10];int n = 9;bool used[10];memset(used,false,sizeof(used));for(int i=0;i<n;i++){a[i] = k / factor[n-1-i];k %= factor[n-1-i];int cnt = 0;for(int j=0;j<n;j++){if(!used[j]) {cnt++;if(a[i] + 1==cnt) {num.push_back(j);used[j] = true;break;}}}}return num;
}int pos[]={8,0,1,2,3,4,5,6,7};int getdis(int a,int b) //曼哈顿距离
{return (abs(a/3-b/3)+abs(a%3-b%3));//a/3-b/3行差,a%3-b%3列差
}int get_evaluation(vector<int>num) //评估函数
{//评估函数设定为1-8八数字当前位置到目标位置的曼哈顿距离之和。 比如其中就有当前位置的1和目标位置的1 的曼哈顿距离之和//目标式1 2 3 4 5 6 7 8 0//此时 pos[1]=0 ,表示在目标式中1原本应该在第0位 从而推出0应该在第8位 对应pos[0] 所以pos的作用是 映射这个数字在目标式子中的位置,所以要偏移//当前 2 4 7 6 8 0 1 3 5//因此 i 表示 在当前式子中这个数字的位置 比如2 是第0位 4 是第1位int h=0;for(int i=;i<9;i++){h+=getdis(i,pos[num[i]]);}return h;
}void solve()
{priority_queue<state>openlist;set<int>closelist; //在closelist中,抛弃该状态。while(!openlist.empty()) openlist.pop();closelist.clear();int h=get_evaluation(stnum);openlist.push(state(h,0,h,cantor(stnum)));int step=0; //统计步数bool flag=false; //标记是否能找到目标状态while(!openlist.empty()){state cur=openlist.top(); //从openlist中取出F值最小的状态openlist.pop();int k=cur.k;//cur的康托展开 closelist.insert(k); //将该状态加入closelist if(destination==k) {//若该状态为目标状态,结束搜索flag=true; //找到目标状态step=cur.g; //步数break;}vector<int> st = recantor(k); //当前状态的八数码序列for(int i=0;i<9;i++) if(st[i]==0) //找到0的位置{if(i%3!=2) { //不在行末,0位可以和右边相邻位置交换swap(st[i],st[i+1]);int x = cantor(st);int g = cur.g+1;int h = get_evaluation(st);int f = g + h;if(closelist.find(x)==closelist.end()) {openlist.push(state(f,g,h,x));}swap(st[i],st[i+1]);//又要换回来来进行下面的步骤}if(i%3!=0) { //不在某行开头,可以和左边相邻位置交换swap(st[i],st[i-1]);int x = cantor(st);int g = cur.g+1;int h = get_evaluation(st);int f = g + h;if(closelist.find(x)==closelist.end()) {openlist.push(state(f,g,h,x));}swap(st[i],st[i-1]);}if(i<6) {swap(st[i],st[i+3]);int x = cantor(st);int g = cur.g+1;int h = get_evaluation(st);int f = g + h;if(closelist.find(x)==closelist.end()) {openlist.push(state(f,g,h,x));}swap(st[i],st[i+3]);}if(i>2) {swap(st[i],st[i-3]);int x = cantor(st);int g = cur.g+1;int h = get_evaluation(st);int f = g + h;if(closelist.find(x)==closelist.end()) {openlist.push(state(f,g,h,x));}swap(st[i],st[i-3]);}}}if(!flag) cout << "No Solution!" << endl;else cout<<step<<endl;}int main() {vector <int> des;for(int i=0;i<8;i++) des.push_back(i+1);des.push_back(0);destination=cantor(des);int T;cin >> T;while(T--){int a;stnum.clear();for(int i=0;i<9;i++) {cin>>a; stnum.push_back(a);}solve();}return 0;
}
通常A* 还有一个更新
对于第n个F
即 if(F>g(n)+1+H(n-1)) F=g(n)+1+H(n-1)