• 康托展开


    康托展开是全排列到自然数的双射,可以用于压缩空间。

    比如有一个全排列,p1p2...pn,那么其康托展开X=a1(n-1)!+a2(n-2)!+...+an0!。其中a1等是指当前未在排列中出现过且小于当前数字的数字个数。这样我们就把一个全排列映射成了一个自然数。

    上述X就表示在所有全排列中,小于该排列的有多少个。其实也就意味着我们可以通过X求出原排列。

    求出X/(n-1)!,就可以知道第一位上的数字,然后令X=X%(n-1)!,不断重复上述过程,直到X=0为止,我们就求出了前n-1位上的数字,而最后一位自然也就确定了。

     1 //需要预处理出n!等
     2 
     3 inline int cantor() { //求某个排列的次序x
     4     int x = 0;
     5     for (int i = 1; i <= n; ++i) {
     6         int small = 0;
     7         for (int j = i + 1; j <= n; ++j)
     8             if (a[j] < a[i]) ++small;
     9         x += small * fact[n - i];
    10     }
    11     return x + 1;
    12 }
    13 
    14 int vis[maxn];
    15 
    16 inline void decantor(int x) { //求第x大排列
    17     memset(vis, 0, sizeof(vis));
    18     --x;
    19     for (int i = 1, j; i <= n; ++i) {
    20         int t = x / fact[n - i];
    21         for (j = 1; j <= n; ++j)
    22             if (!vis[j]) {
    23                 if (!t) break;
    24                 --t;
    25             }
    26         printf("%d ", j);
    27         vis[j] = 1;
    28         x %= fact[n - i];
    29     }
    30     return;
    31 }

    NOIP2004普及组有道题:火星人(在洛谷上的链接:https://www.luogu.org/problemnew/show/P1088),当时第一反应就是康托展开,但比较懒,写了个STL就水过去了,后来尝试用康托展开,发现并不行,因为这道题N很大,但M较小,假若N较小,M很大,我想康托展开还是很实用的。但这里也给出代码。

     1 #include <cstdio>
     2 #include <cstring>
     3 
     4 const int maxn = 1e4 + 5;
     5 
     6 int n, m, a[maxn];
     7 long long fact[maxn];
     8 
     9 inline int cantor() { //求某个排列的次序x
    10     int x = 0;
    11     for (int i = 1; i <= n; ++i) {
    12         int small = 0;
    13         for (int j = i + 1; j <= n; ++j)
    14             if (a[j] < a[i]) ++small;
    15         x += small * fact[n - i];
    16     }
    17     return x + 1;
    18 }
    19 
    20 int vis[maxn];
    21 
    22 inline void decantor(int x) { //求第x大排列
    23     memset(vis, 0, sizeof(vis));
    24     --x;
    25     for (int i = 1, j; i <= n; ++i) {
    26         int t = x / fact[n - i];
    27         for (j = 1; j <= n; ++j)
    28             if (!vis[j]) {
    29                 if (!t) break;
    30                 --t;
    31             }
    32         printf("%d ", j);
    33         vis[j] = 1;
    34         x %= fact[n - i];
    35     }
    36     return;
    37 }
    38 
    39 int main() {
    40     scanf("%d%d", &n, &m);
    41     fact[0] = 1;
    42     for (int i = 1; i <= n; ++i) fact[i] = fact[i - 1] * i;
    43     for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    44     decantor(cantor() + m);
    45     return 0;
    46 }
    康托展开(未AC)

    总不能这样水过去,其实就是求出一个排列之后的某个排列,可以说是一种枚举排列的方法。直接给出代码。

     1 #include <cstdio>
     2 #include <algorithm>
     3 
     4 using namespace std;
     5 
     6 const int maxn = 1e4 + 5;
     7 
     8 int n, m, a[maxn];
     9 
    10 inline void add() {
    11     for (int i = n - 1; i >= 1; --i) {
    12         if (a[i] > a[i + 1]) continue;
    13         for (int j = n; j >= 1; --j) {
    14             if (a[j] < a[i]) continue;
    15             swap(a[j], a[i]);
    16             sort(a + i + 1, a + n + 1);
    17             return;
    18         }
    19     }
    20 }
    21 
    22 int main() {
    23     scanf("%d%d", &n, &m);
    24     for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    25     for (int i = 1; i <= m; ++i) add();
    26     for (int i = 1; i <= n; ++i) printf("%d ", a[i]);
    27     return 0;
    28 }
    AC代码
  • 相关阅读:
    GUID概念
    某猿的饭局
    SVN切分支步骤
    OSX:设置用户默认浏览器
    值得推荐的android开发框架简单介绍
    用实力让情怀落地!阅兵前线指挥车同款电视TCL&#160;H8800受捧
    Excel查询序列所相应的值-vLoopup函数,求比例分子改变但分母不变
    CSS3制作W3cplus的关注面板
    Spring MVC框架实例
    @property 和@synthesize
  • 原文地址:https://www.cnblogs.com/Mr94Kevin/p/9764241.html
Copyright © 2020-2023  润新知