About the use and writing of Cantor's expansion

When dealing with problems such as eight-digit numbers that require full permutation, storage is often a problem, because there are only n! cases, but the length of the number is n, and it is definitely not possible to use an array. At this time, Cantor expansion comes in handy. Of course, map is a good way to deal with it when conditions allow.
Cantor Expansion: Write the integers satisfying 0 <= P < (n+1)! as P = an*n! + an-1 * (n-1)! + ... + a2 * 2! + a1 * 1 The form !(0 <= ai < i, i = 1,2,3...) is called the Cantor expansion of P.
Then for an ordinary number, we can carry out the Cantor expansion of it in O(L) time, as long as we keep trying to divide by i!.

But first we need to preprocess the array of factorials:

int fac[15];
void first()
{
    fac[0] = 1;
    for (int i = 1; i <= 12; ++i)
    fac[i] = fac[i-1] * i;
}

We are now only considering numbers in the int range, so 12! = 479001600 is the limit.

void cantor(int x)
{
    int s[15], st;
    printf("%d = ", x);
    for (int i = 12; i; i--)
    if (fac[i] <= x)//求最高位
    {
        st = i;
        break;
    }
    for (int i = st; i; i--)
    {
        s[i] = x / fac[i];//s[i]记录当前位的数字
        x -= s[i] * fac[i];
    }
    for (int i = st; i; i--)
    {
        printf("%d*%d!", s[i], i);
        if (i != 1) printf(" + ");
    }
    putchar('\n');
}

However, it is meaningless to only expand the Cantor of a number. What we need to deal with is a full permutation of n. To turn this permutation into a ranking, we need to use the method of inverting pairs.
For example, for the full permutation of 9, 564893217, its corresponding ranking is 184190, among which:
for the first 5, there are four numbers 4, 3, 2, and 1 that are smaller than it, and at this time, there are 8 permutations at the beginning, so ans += 4 * 8!;
then 6, followed by 4, 3, 2, 1, and then there are 7! at the beginning, so ans += 4 * 7!;
and so on, there are 184190 = 4 * 8! + 4 * 7! + 3 * 6! + 4 * 5! + 4 * 4! + 2 * 3! + 1 * 2! + 0 * 1!; so you can get the code:

int cantor_change(int x)
{
    int s[15], len = 0, ans = 0;
    while(x)
    {
        s[++len] = x % 10;
        x /= 10;
    }//计算x的位数
    for (int i = len; i; i--)
    {
        int tmp = 0;
        for (int j = i - 1; j; j--)
        if (s[i] > s[j]) ++tmp;//求逆序对个数
        ans += fac[i-1] * tmp;
    }
    return ans;
}

Of course, new valuable problems also arise. For a full permutation of n, find the permutation with the largest xth. 
This is the inverse operation of Cantor's expansion, which can be used to restore the chessboard when dealing with the eight-digit problem. 
For a full permutation of n, assuming that we have fixed the first item, then there are (n-1)! kinds of remaining permutations, and the first item has n choices, then when the first item is m, the The ranking of permutations is between (m-1)*(n-1)! to m*(n-1)!, and this can be generalized, and it is also valid for the following n-2, n-3, etc. Therefore, every time we divide x by (ni)!, we remember that the quotient is k, then there are k items in the following i items that are smaller than the current item, we can use an array to mark the numbers that have been used, each time before Find the number that meets the current requirements from the marked numbers. 
Example: x = 2048, n = 9;
 first, x / 8! = 0, so the first bit is 1; 
x / 7! = 0, the second bit is 2; 
x / 6! = 2, then there are two The number is less than the current bit, it should be 3 and 4, so the current bit is 5, x %= 6! = 608; 
x / 5! = 5, the latter 3, 4, 6, 7, and 8 are all smaller than the current bit, so is 9, x %= 5! = 8;
and so on, the final arrangement can be obtained: 125936487;

void cantor_inv(int x, int n)
{
    x--;
    int flag[10], s[10];
    for (int i = 1; i <= n; ++i)
    flag[i] = 0;
    for (int i = 1; i <= n; ++i)
    {
        int tmp = x / fac[n-i], now = 0;
        x %= fac[n-i];
        for (int j = 1; j <= n; ++j)
        {
            if (!flag[j])
            {
                if (now == tmp)
                {
                    flag[j] = 1;
                    s[i] = j;
                    break;
                }
                ++now;
            }
        }
    }
    for (int i = 1; i <= n; ++i)
    printf("%d", s[i]);
    putchar('\n');
}

Then, there is a new way to deal with the arrangement, so there is no need to worry about the problem of space explosion.

Recommended exercise: codevs2037 magic board

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326384664&siteId=291194637