1. 程式人生 > >hdu1027(逆康托展開)

hdu1027(逆康托展開)

自然 vector ear 表示 using src 全排列 排列 void

src:http://acm.hdu.edu.cn/showproblem.php?pid=1027

一開始已經提過了,康托展開是一個全排列到一個自然數的雙射,因此是可逆的。即對於上述例子,在(1,2,3,4,5)給出61可以算出起排列組合為 34152。由上述的計算過程可以容易的逆推回來,具體過程如下:

  • 用 61 / 4! = 2余13,說明a[5]=2,說明比首位小的數有2個,所以首位為3。
  • 用 13 / 3! = 2余1,說明a[4]=2,說明在第二位之後小於第二位的數有2個,所以第二位為4。
  • 用 1 / 2! = 0余1,說明a[3]=0,說明在第三位之後沒有小於第三位的數,所以第三位為1。
  • 用 1 / 1! = 1余0,說明a[2]=1,說明在第二位之後小於第四位的數有1個,所以第四位為5。
  • 最後一位自然就是剩下的數2啦。
  • 通過以上分析,所求排列組合為 34152。

!!!用61/4!不用擔心受到3!這一項的影響,因為這一樣的值不可能>=4!,因為3!這一項的系數表示第四位前面小於第四位的,易知這個值<=3 !!!

註意:cantors算法給出的序列全排列順序的下標是從0開始的,所以從題目輸入序號後要減一!!!

ac代碼:

#include<bits/stdc++.h>
using namespace std;
#define per(i,a,b) for(int i=a;i <= b;i++)
#define
max(a,b) a=max(a,b) #define min(a,b) a=min(a,b) #define sz(x) (int)x.size() typedef long long ll; ll gcd(ll a,ll b){while(b){ll t=b;b=a%b;a=t;}return a;} const int inf=0x3f3f3f3f; const int mod=1000000007; #define siz 40005 static const int FAC[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880}; // 階乘 int n,m; vector
<int>v; void decantor() { vector<int>chs;//存放可選數 for(int i=1;i<=n;i++)chs.push_back(i); int cnt=n; while(cnt>0){ if(cnt<=8){ int r=m/FAC[cnt-1]; m%=FAC[cnt-1]; v.push_back(chs[r]); chs.erase(chs.begin()+r); } else { v.push_back(chs.front()); chs.erase(chs.begin()); } cnt--; } } int main() { #ifndef ONLINE_JUDGE freopen("b\\Data_In.txt","r",stdin); #endif while(scanf("%d%d",&n,&m)!=EOF){ m--; v.clear(); decantor(); for(int i=0;i<v.size();i++)printf("%d%c",v[i]," \n"[i!=(v.size()-1)?0:1]); } return 0; }

hdu1027(逆康托展開)