康拓展开以及逆展开 板子

讲的很好的一篇博客, 原理之类的

康托展开: (板子)
fac是存的阶乘数, s是当前我们需要求的这个排列. 返回值为康托值+1, 即第几排列.

ll fac[15];
int kang(string s) {
    int len = sz(s); fac[0] = 1;
    for (int i = 1 ; i <= len ; i ++) {
        fac[i] = fac[i-1] * i;
    }
    ll ans = 0;
    for (int i = 0 ; i < len ; i ++) {
        int cnt = 0;
        for (int j =  i + 1 ; j < len ; j ++) {
            if (s[j] < s[i]) cnt++;
        }
        ans += fac[len - i - 1] * cnt;
    }
    return ans+1;
}

解释版:

ll fac[15];
int kang(string s) {
    int len = sz(s); fac[0] = 1;
    for (int i = 1 ; i <= len ; i ++) {
        fac[i] = fac[i-1] * i;
    }
    ll ans = 0;
    for (int i = 0 ; i < len ; i ++) {
        int cnt = 0; // 从0开始计数.
        for (int j =  i + 1 ; j < len ; j ++) {
            if (s[j] < s[i]) cnt++;
        }
        ans += fac[len - i - 1] * cnt;
    } // ans 是康拓展开值.
    return ans+1; // 因为计数是从0开始的, 也就是我们算的ans是
    // 比当前这个排列小的排列数. 所以当前这个排列就是第abs+1大.
}

分界线 —— 嘤嘤嘤

康拓逆(inverse)展开: (板子)

ll fac[15], vis[15], c[15];
void vkang(ll m, ll k) {
    --k; Fill(vis, 0); fac[0] = 1;
    for (int i = 1 ; i <= m ; i ++) {
        fac[i] = fac[i-1] * i;
    }
    for (int i = 1 ; i <= m ; i ++) {
        ll t = k/fac[m-i]; int j;
        for (j = 1 ; j <= m ; j ++) {
            if (!vis[j]) {
                if (!t) break;
                --t;
            }
        }
        vis[j] = 1;
        c[i] = j;
        k %= fac[m-i];
    }
    for (int i = 1 ; i <= m ; i ++) {
        printf("%d%c", c[i], i == m ?'\n':' ');
    }
}

带偏移量的逆展开: (即我们只求那几个小的排列情况, 然后通过加偏移量来确定那几个数的具体排列情况)

ll fac[15], vis[15], c[15];
ll n;
void vkang(ll m, ll k) {
    --k; Fill(vis, 0);
    for (int i = 1 ; i <= m ; i ++) {
        ll t = k/fac[m-i]; int j;
        for (j = 1 ; j <= m ; j ++) {
            if (!vis[j]) {
                if (!t) break;
                --t;
            }
        }
        vis[j] = 1;
        c[i] = n - m + j; // 就是这加了一个偏移量. n个数字的最后m个排列情况.
        k %= fac[m-i];
    }
    for (int i = 1 ; i <= m ; i ++) {
        printf("%d%c", c[i], i == m ?'\n':' ');
    }
}

解释版:

ll fac[15], vis[15], c[15];
void vkang(ll m, ll k) { // 因为计数是从0开始的, 所即排列数应该-1.
    --k; Fill(vis, 0); fac[0] = 1;
    for (int i = 1 ; i <= m ; i ++) {
        fac[i] = fac[i-1] * i;
    }
    for (int i = 1 ; i <= m ; i ++) {
        ll t = k/fac[m-i]; int j;
        for (j = 1 ; j <= m ; j ++) {
            if (!vis[j]) {
                if (!t) break;
                --t;
            }
        }
        vis[j] = 1; // vis数组标记当前这个数字是否用过.
        c[i] = j;
        k %= fac[m-i];
    }
    for (int i = 1 ; i <= m ; i ++) {
        printf("%d%c", c[i], i == m ?'\n':' ');
    }
    // c数组存的是当前这个排列具体的数字.
}

你可能感兴趣的:(康拓展开)