第K个幸运排列 (51Nod-1635)
題目
比得喜歡幸運數字。這里所說的幸運數字是由4和7組成的正整數。比如,整數47,744,4是幸運數字,而5,17,467就不是。
一天比得夢到由數字1到n組成的第K個字典序排列。要求計算在這個排列中有多少個幸運數所在的位置的編號也是幸運數。
舉例如下:
比如排列[1,2,3,4],其中4為幸運數,它所在的位置的下標為4(幸運數),所在此排列中,結果為1.
又如,[1,2,4,3],4所在位置為3。3不是幸運數,所以,結果為0.
樣例解釋:
排列是由n個元素組成的一個序列,在這個序列中,整數1到n都要有且僅出現一次。
在排列中,第i個元素定義為 aiai (1≤i≤n)。
假定有排列a,和排列b。長度均為n。即都是由1到n組成。如果存在i(1≤i≤n)和對于任意j(1≤j<i)使得 ai < biai < bi 且 aj = bjaj = bj 。我們就說,a的字典序比b的字典序小。
對1到n組成的所有排列進行字典序升序排列。然后取其中的第K個排列,就是第K個字典序排列。
在樣例中,最終排列如下:
1 2 3 4 6 7 5
只有第4個位置是幸運數。
輸入
單組測試數據
共一行,包含兩個整數n和k(1≤n,k≤10^9)表示排列中的元素個數,和第K個字典序排列。
輸出
如果k超過出了1到n所有排列數的種數,那么輸出“-1”(沒有引號)。
否則,輸出題目要求結果。
輸入樣例
7 4
輸出樣例
1
思路:
題目本質是要求 n 個數的排列的第 k 小排列,而且要求排列中?a[i] 與 i 均為?4 或 7
1~n 的排列是 n!,而階乘的增長是極大的,可以發現,在 1E9 的范圍內,n 只要超過 13 就超過了最大范圍
因此,可以將 1~n 的序列分為兩部分,以滿足 pos!>=k 為分界,前半部分是 1~n-pos,后半部分是 n-pos+1~n
分成兩個部分后,可以確定是,前半部分一定是順序排列,此時用 dfs 來處理,只需要考慮有多少數字包含 4、7,后半部分對 k 做逆康托展開即可
源程序
#include<iostream> #include<cstdio> #include<cstdlib> #include<string> #include<cstring> #include<cmath> #include<ctime> #include<algorithm> #include<utility> #include<stack> #include<queue> #include<vector> #include<set> #include<map> #include<bitset> #define EPS 1e-9 #define PI acos(-1.0) #define INF 0x3f3f3f3f #define LL long long #define Pair pair<int,int> const int MOD = 1E9+7; const int N = 6000+5; const int dx[] = {0,0,-1,1,-1,-1,1,1}; const int dy[] = {-1,1,0,0,-1,1,-1,1}; using namespace std;LL fac[20]; LL num[20]; LL res; int vis[20];void init(LL n) {fac[0]=1;for(int i=1; i<=n; i++)fac[i]=fac[i-1]*i; } bool check(LL x) { //判斷是不是幸運數while(x) {LL cnt=x%10;if(cnt!=4 && cnt!=7)return false;x/=10;}return true; } void revContor(LL n,LL k,int base) { //逆康托展開k--;for(LL i=1; i<=n; i++) {LL cnt=k/fac[n-i];k=k%fac[n-i];for(LL j=1; j<=n; j++) {if(!vis[j]) {if(!cnt) {vis[j]=1;num[i]=j+base;break;}cnt--;}}}for(LL i=1; i<=n; i++) //判斷展開式中多少幸運數并且位置也是幸運數if(check(i+base) && check(num[i]))res++; } void DFS(LL x,LL n) { //DFS尋找到n有多少個幸運數if(x>n)return ;if(x)res++;DFS(x*10+4,n);DFS(x*10+7,n); } int main() {LL n,k;scanf("%lld%lld",&n,&k);init(n);if(n<13 && fac[n]<k ) { //沒有第k個排列printf("-1\n");return 0;}LL pos=1;int i=1;while(fac[i]<k){pos=i;i++;} // for(LL i=1; ; i++) { // if(fac[i]>=k) { // pos=i; // break; // } // }revContor(pos,k,n-pos);DFS(0,n-pos);printf("%lld\n",res);return 0; }?
總結
以上是生活随笔為你收集整理的第K个幸运排列 (51Nod-1635)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 信息学奥赛一本通(1013:温度表达转化
- 下一篇: 理论基础 —— 队列