第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;
}

 

posted @   老程序员111  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示