简单排序——幸运数字(康托展开)

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K

一、题目内容

题目描述

定义一个数字为幸运数字当且仅当它的所有数位都是4或者7。
比如说,47、744、4都是幸运数字而5、17、467都不是。
现在想知道在1...n的第k小的排列(permutation)中,有多少个幸运数字所在的位置的序号也是幸运数字。

输入描述

第一行两个整数n,k。
1 <= n,k <= 1000,000,000

输出描述

一个数字表示答案。
如果n没有k个排列,输出-1。

示例1

输入

7 4

输出

1

说明

第4小的排列:1 2 3 4 6 7 5

示例2

输入

4 7

输出

1

说明

2 1 3 4

二、解题思路

依据题意:1、幸运数字为由4或7组成;2、1~n的第k小排列;上述两个条件成立成立即输出数列中幸运数字的位置序号也是幸运数字的个数。
第一种条件,我们直接逐一比较即可。针对第二种条件,我们知道一个容量为n的数组全排列的所有可能性为n!种,题目中的k只有1e9,因为13!已经大于1e9,所以即使n再大,会变的也只有后面的13位,前面的位置和数字都是一样的。由此我们联想到逆康托展开

康托展开相关概述:

举个栗子,对于1~5的一个全排列[1,2,3,4,5]和[5,4,3,2,1],从字典序而言,前者是该全排列集的第一个,后者是该集的最后一个。那么康托展开,即给定一个 n 位数的全排列,我们可以根据康托展开公式确定其应当是字典序中的第“几”个全排列。
由于康托展开计算的是某个全排列方式在该全排列集合中的字典序,其映射关系唯一且单调,故该映射关系是可逆的。即,我们给定一个全排列的所有字符,以及某个字典序号,我们可以利用逆康托展开得到相应的那个全排列。
下图为康托展开运算公式,其中ai为整数,且0 <= ai < i,1 <= i <= n;


康托展开运算
康托展开

以[3,4,1,5,2](数组a)为例,计算他的康托展开值(字典序)

  1. 首位为3,则小于3的数由两个,为1、2,a[5] = 2,则首位小于3的所有排列组合为a[5] * (5 - 1)!
  2. 第二位为4,由于第一位小于4,1、2、3种一定会有1个充当第一位,因此排在4之下的只剩2个,所以其实计算的是在第二位之后小于4的个数。因此a[4] = 2;
  3. 第三位是1,则在其后小于1的数有0个,因此a[3] = 0;
  4. 第四位是5,则在其后小于5的数有1个,为2,因此a[2] = 1;
  5. 最后一位,由于在它之后已经没有数了,因此a[1]固定为0;

根据公式:
X = 2 * 4! + 2 * 3 ! + 0 * 2! + 1 * 1! + 0 * 0! = 61
因此比数组[3,4,1,5,2]小的组合有61个。

逆康托展开

这里我们用到逆康托展开,以[3,4,1,5,2](数组a)为例。这里输入和输出互反,同时我们需要输入全排列的字符个数n。(以下用到数组a)
我们通过康托展开公式得到数组a的排位为:61

  1. 用 61 / 4! = 2余13,说明a[5] = 2, 说明比首位小的数由2个,所以首位为3。
  2. 用 13 / 3! = 2余1,说明a[4] = 2,说明在第二位之后小于第二位的数有2个,所以第二位为4。
  3. 用1 / 2! = 0余1,说明a[3] = 0,说明在第三位之后没有小于第三位的数,所以第三位为1。
  4. 用 1 / 1! = 1余0,说明a[2] = 1,说明在第四位之后小于第四位的数有1个,所以第四位为5。
  5. 最后一位自然数就是剩下的数2。
  6. 综上所述,所求的排列组合为[3,4,1,5,2]。
因此在这里我们分为两部分解决:
  1. 当n<=13时,因为存在k>n!,因此增加特殊判断:如果k<n!,那么我们可以用逆康托展开直接得到地k个序列,然后逐一判断即可。
  2. 当n>13时,从1~n-13都是位置和数字一样,只有后面的13位会发生改变,同样的我们使用逆康托展开运算即可求出答案。

代码实操

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long LL;
LL f[20];
int n,m,s;
void init() {
    LL I;
    f[0] = 1;
    for(i = 1; i < 15; i++) {
        f[i] = f[i - 1] * I;
    }
}
int check(int x) {
    while(x) {
        if (x % 10 != 4 && x % 10 != 7)
            return 0;
        x = x / 10;
    }
    return 1;
}
void dfs(LL x, LL y) {
    if (x > y) 
        return;
    if (x != 0)
        s++;
    dfs(x * 10 + 4, y);
    dfs(x * 10 + 7, y);
}
int solve(int x, int y) {
    vector<int> p;
    vector<int> q;
    int i, j, k;
    for(i = y; i <= n; i++) {
        p.push_back(i);
    }
    for(i = n - y + 1; i >= 1; i--) {
        j = x % f[i - 1];
        k = x / f[i - 1];
        x = j;
        q.push_back(p[k]);
        p.erase(p.begin() + k);
    }
    k = q.size();
    j = 0;
    for(i = 0; i < k; i++) {
        if (check(q[i]) && check(i + y)) {
            j++;
        }
    }
    return j;
}
int main() {
    int i, j, ans;
    init();
    scanf("%d%d",&n,&m);
    if (n < 15 && f[n] < m) {
        printf("-1\n");
        return 0;
    }
    m--;
    ans = 0;
    s = 0;
    if (n >= 15) {
        dfs(0, n - 14);
        ans += s;
        ans += solve(m, n - 14);
    } else {
        ans = solve(m, 1);
    }
    printf("%d\n",ans);
    return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342