1. 程式人生 > >關於數論【康托展開及其逆運算】

關於數論【康托展開及其逆運算】

-i mes 訪問 一個 3*3 關於 font color 多次

表示這個東西背了很多次,但是次次忘,希望這次能夠記住吧。

康托展開:
問45231是n=5的全排列中第幾個排列?
ans:= 3*4! + 3*3! + 1*2! + 1*1! + 0*0! =93
這時求出的是在45231前面全部的排列,排名還要加1
所以對此的做法,就是將階乘前面的求出來,這個就是在a[i]前面,還沒出現過的數字。比如4前面1~3都沒出現而1(或2或3)xxxx肯定在4xxxx前面,因為有四個不定的數字,所以乘上4!

逆運算:
問n=5的全排列中第94個是誰?94先-1
94/4!=3.875
所以第一個數字前面有3個數字
9
4先減3*4!=22
22/3!=3.6666666666666666666666666666667


前面還是有三個,因為4之前用過了,所以是5
由此類推。

模板題caioj1220:

#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
int n,a[20];
LL jc[20];
bool bo[20];
void kangtuo1()
{
    jc[0]=1;for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        jc[i]=jc[i-1]*i;
    }
    LL ans
=0;//有多少個這個全排列前面的 memset(bo,false,sizeof(bo));//這個有沒有在前面出現過 for(int i=1;i<=n-1;i++) { int k=0;//k表示前面有多少個沒被訪問過的 for(int j=1;j<a[i];j++) if(bo[j]==false)k++; bo[a[i]]=true;//當前這個數被訪問過 ans+=k*jc[n-i];//乘以當前個數的階乘 } printf("%lld\n",ans+1
); } void kangtuo2() { LL ans; scanf("%lld",&ans);ans--; memset(bo,false,sizeof(bo)); for(int i=1;i<=n;i++) { LL k=ans/jc[n-i];//有多少個比第i個位置小的數 ans-=k*jc[n-i]; for(int j=1;j<=n;j++) if(bo[j]==false) { if(k==0) { a[i]=j; bo[a[i]]=true; break; } k--; } } for(int i=1;i<n;i++)printf("%d ",a[i]); printf("%d\n",a[n]); } int main() { scanf("%d",&n); kangtuo1(); kangtuo2(); return 0; }

關於數論【康托展開及其逆運算】