关于数论【康托展开及其逆运算】
生活随笔
收集整理的這篇文章主要介紹了
关于数论【康托展开及其逆运算】
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
表示這個東西背了很多次,但是次次忘,希望這次能夠記住吧。
康托展開:
問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個數字
94先減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; }?
轉載于:https://www.cnblogs.com/AKCqhzdy/p/7612601.html
總結
以上是生活随笔為你收集整理的关于数论【康托展开及其逆运算】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一个简单粗暴的爬虫 - 必应今日美图
- 下一篇: java用毫秒数做日期计算的一个踩坑记录