排列的字典序問題Permrank題解
先附上n=4的時候的4*3!=24種情況
1 23 4 1 2 4 3 1 3 2 4 1 3 4 2 1 4 2 3 1 4 3 2
2 13 4 2 1 4 3 2 3 1 4 2 3 4 1 2 4 1 3 2 4 3 1
3 12 4 3 1 4 2 3 2 1 4 3 2 4 1 3 4 1 2 3 4 2 1
4 12 3 4 1 3 2 4 2 1 3 4 2 3 1 4 3 1 2 4 3 2 1
這裡現注意第一點,1 2 3 4序號為0,4 3 2 1的序號為15
不需要全排列出來然後一個一個判斷,那樣可能會爆時間,這裡有個更好的規律方法
這裡舉個2 3 4 1的例子
1 23 4 1 2 4 3 1 3 2 4 1 3 4 2 1 4 2 3 1 4 3 2
2 13 4 2 1 4 3 2 3 1 4 2 3 4 1 2 4 1 3 2 4 3 1
3 12 4 3 1 4 2 3 2 1 4 3 2 4 1 3 4 1 2 3 4 2 1
4 12 3 4 1 3 2 4 2 1 3 4 2 3 1 4 3 1 2 4 3 2 1
“2 3 4 1”從前往後迴圈,現寫出2 3 4 1的序號:
2 3 4 1=(2-1)*3!+(2-1)*2! +(2-1)*1! +1 -1=6+2+1+1-1=9
從第一位2開始迴圈,判斷第a[i]位在i後面所有的數中大了多少個,像2在後面是大了一個自己是第二個(倒數第幾,這種意思只可意會不可言傳= =),然後(2-1)。其實這個a[i]大了後面1個就是(2-1)=1這個1,這兩個意思是一樣的。思路是一樣的
例如這個2,(2-1)*3!就是6,就除去了所有1開頭的6個數!!保留了其他的。
這樣實質就是每次減去一種情況,再減去一種情況......就留下來了我們所需要的第幾位數
但是可以優化:
for (int f=1;f<=x-1;f++)
if (a[z]>a[f]) y - -;
從前往後面找,比如2 3 4 1 中的3,從第一位找到i-1位,只要比a[i]小,那麼就y - -,這樣子就求出來了ronaldo函式+1的值。
這裡用一個函式來求ronaldo(鄙人喜歡C羅CristianoRonaldo)
sum+=ronaldo(i,a[i],i)*j[i-1]
這裡j[i]是階乘,因為考試時候這道題範圍是n<=13;這就是專門
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
using namespace std;
long long a[14],j[15]={1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800,479001600};
int n,sum=0;
int ronaldo(int x,int y,int z)
{
if (x==1) return a[x]-1;
if (x==n) return 1;
for (int f=1;f<=x-1;f++)
if (a[z]>a[f]) y--;
return y-1;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
for (int i=1;i<=n;i++)
sum=sum+ronaldo(i,a[i],i)*j[n-i];
cout<<sum-1<<endl;//因為從0開始算第一個
next_permutation(a+1,a+n+1);
for (int i=0;i<=n-1;i++)
cout<<a[i]<<" ";
}
給我打表用的- -
然後由上述式子迴圈一遍後輸出sum-1 因為第一個序號為0
最後用next_permutation(a,a+n) 這裡不知道怎麼從1開始,所以我的陣列從1開始只好向前推一位,然後用next_permutation(a,a+n);這個函式很強大的
By Carry