您的位置:首页 > 其它

关于全排列问题的总结(康托展开)

2014-09-13 21:46 253 查看
  今天做USACO上一道题的时候用到了这个东西,当时觉得麻烦直接用map写了,然后仔细研究了一下,康托展开与其逆展开,个人觉得会在节省空间和表示状态上有比较大的帮助。

  先来正展开吧,即给定排列序列,求是第几个排列,看序列中的第K位,假设序列长度为N,那么,K位之后的各位数全排列有(N-K)!种,然后看K之后的序列中有几个比K小的,

此时,把K*(N-K)!加入到计数变量即答案中即可,一个一个枚举K。

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
char num[10];
int jie(int n)
{
if(n==0) return 1;
int r=1;
for(int i=2;i<=n;i++) r*=i;
return r;
}
int main()
{
int t;
while(cin>>t){
sprintf(num,"%d",t);
int r=0,pos=0;
int len=strlen(num);
for(int i=0;i<len;i++){
int temp=0;
for(int j=i+1;j<len;j++) if(num[j]<num[i])
temp++;
r+=temp*jie(len-i-1);
}
cout<<r+1<<endl;
}
return 0;
}


  然后是康托逆展开,反过来,给定排列数,求序列,首先,我们每确定一位,就将这位上的数字标记,防止重读,然后就是对于第K位,假设序列长度为N,那么,K后边的序列的排列数是(N-K)!种,然后K是从小到大增加的,再设temp表示比第k位应放数字小的数字有几个,给定排列数为t,那么temp=t/(N-K)!,然后我们只要数到第temp+1个未被标记的数字填进来就可以了。注意更新t,还有就是可能为什么要选temp+1个未被标记的数这里,因为temp得到的是共有多少个(N-K)!,此时t-temp这个值哪怕是0,他也不属于temp那个值产生的序列了,啊啊啊,说的不是特别清楚,郁闷

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
char num[100];
bool is[10];
int jie(int n)
{
if(n==0) return 1;
int r=1;
for(int i=2;i<=n;i++) r*=i;
return r;
}
int main()
{
int t,len;
while(cin>>t>>len){
memset(num,0,sizeof(num));
memset(is,false,sizeof(is));
t--;
for(int i=1;i<=len;i++){
int temp=t/jie(len-i),j,total;
t-=temp*jie(len-i);
for(j=1,total=0;total<=temp;j++) if(!is[j])
total++;
j--;
is[j]=true;
num[i-1]='0'+j;
}
cout<<num<<endl;
}
return 0;
}


 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  全排列 康托展开