康拓展开
已知有一集合A包含n个不同的元素,其中(k1,k2,k3...,kn-2 )是A的一个排列。假设此排列为A按字典序从小到大排列的排列中的第x个排列,则x=a1(n-1)!+a2(n-2)!+...an-2*1!+an-1*0! (其中ai为ki+1...kn中比ki小的数的个数)
例如:
3214是1234的第2*3! +1*2! +0*1! +0*0! =14 个排列。
实现代码:
int cantos(){//康拓展开函数部分
memset(used,false,sizeof(used));used记录一个数是否已用
int ans=0;
for(int i=1;i<=n;i++){
int cnt=0;
for(int j=1;j<a[i];j++){
if(!used[j]){//累加比a[i]小的数
++cnt;
}
}
ans+=cnt*fact(n-i);//fact函数计算阶乘
used[a[i]]=true;
}
return ans;
}
康拓逆展开
已知有一集合A包含n个不同的元素,如果按字典序从小到大排序,A的排列S是第x个排列,
则:s1=x/(n-1)! (取整)
s2=x%(n-1)!/(n-2)!
......
sn=0 (si表示ki+1,ki+2...kn中比ki小的数的个数)
例如:
A包含4个元素,S是A的第14个排列。
s1=14/6=2
s2=14%6/2=1
s3=14%6%2/1=0
s4=0
实现代码:
int main() {
memset(used,false,sizeof(used));
cin>>n>>x;
for(int i=1;i<=n;i++){
int ai=x/fact(n-i)+1;//fact函数计算阶乘
x%=fact(n-i);
for(int j=1;j<=n;j++){
if(!used[j]){//used记录这个数有没有用过
--ai;
if(ai==0){
p[i]=j;
used[j]=true;
break;
}
}
}
}
for(int i=1;i<=n;i++){
cout<<p[i]<<" ";
}
cout<<endl;
return 0;
}
应用
康拓展开主要用来标记一个排列。
如果我们直接用整数来表示排列,那么对于有9个数的排列,你要开一个a[987654321]这么大的数组;
而如果我们用康拓展开,只需要开a[362800]这么大。
ps:如果数据过大,有时直接不用康拓展开空间更小。
经典题型
8数码问题:
8方块移动游戏要求有1~8个数字方块和一个空方块(用0表示),每一步可以将空方块与相邻方块互换。现在给出起始状态和目标状态,要求用最少的步数从起始状态转换成目标状态,无解输出-1。
用康拓展开记录状态,分别编号,然后直接广搜即可。
AC code:
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
int a[11],s,t,fact[11],d[4]={-3,3,-1,1};
bool b[362885],used[11];
struct data{
int x,step;
};
queue<data> q;
int can(){
memset(used,0,sizeof(used));
int ans=0;
for(int i=1;i<=9;i++){
int cnt=0;
for(int j=1;j<a[i];j++){
if(!used[j])cnt++;
}
ans+=cnt*fact[9-i];
used[a[i]]=1;
}
return ans;
}
int main(){
fact[0]=1;
for(int i=1;i<=9;i++){
cin>>a[i];
if(a[i]==0)a[i]=9;
fact[i]=fact[i-1]*i;
}
s=can();//起始状态编号
b[s]=1;
q.push((data){s,0});
for(int i=1;i<=9;i++){
cin>>a[i];
if(a[i]==0)a[i]=9;
}
t=can();//目标状态编号
bool flag=0;
while(!q.empty()){
memset(used,0,sizeof(used));
data u=q.front();
q.pop();
if(u.x==t){
cout<<u.step;
flag=1;
break;
}
int now;
int x=u.x;
for(int i=1;i<=9;i++){//转换为数组
int ai=x/fact[9-i]+1;
x%=fact[9-i];
for(int j=1;j<=9;j++){
if(!used[j]){
--ai;
if(ai==0){
a[i]=j;
if(j==9)now=i;
used[j]=1;
break;
}
}
}
}
for(int i=0;i<4;i++){
int nx=now+d[i];
if(i==2&&now%3==1)continue;
if(i==3&&now%3==0)continue;
if(nx>=1&&nx<=9){
a[now]^=a[nx];//位运算交换两数
a[nx]^=a[now];
a[now]^=a[nx];
int bian=can();
if(b[bian]){
a[now]^=a[nx];
a[nx]^=a[now];
a[now]^=a[nx];
continue;
}
b[bian]=1;
q.push((data){bian,u.step+1});
a[now]^=a[nx];
a[nx]^=a[now];
a[now]^=a[nx];
}
}
}
if(!flag)cout<<"-1";
return 0;
}