1 条题解

  • 3
    @ 2022-10-5 9:25:43

    题解

    思路1

    这是最常用的思路,我们观察可以发现这是一个全排列,+1 + 1 就相当于下一个全排列。

    我们可以用系统函数next_permutation

    #include<bits/stdc++.h>
    using namespace std;
    int n, m, ord[10005];
    int main()
    {
    	scanf("%d%d", &n, &m);
    	for (int i = 1; i <= n; i++)
    	    scanf("%d", &ord[i]);
    	for (int i = 1; i <= m; i++)
    	    next_permutation(ord + 1, ord + n + 1); // +1
    	for (int i = 1; i <= n; i++)
    	    printf("%d ", ord[i]);
    	printf("\n");
    	return 0;
    }
    

    当然你也可以选择手写,用dfs,这里就不出示代码了。

    思路2

    我们没事找事拓宽思维,可以把全排列转换为变进制数。

    对于第 ii 根手指,它有 ni+1n-i+1 种选择,根据位值原理,要想让每个数对应一个全排列,就要让这一位数是 ni+1n-i+1 进制的。

    之后我们把变进制数加上 mm,再转换回来,就可以了。

    我们来看一个实例: 将 1,4,5,2,31,4,5,2,3 变成变进制数:

    • 111155 种选择 {1,2,3,4,5}\{1,2,3,4,5\} 的第 11 种,故变为 00(从0开始)
    • 224444 种选择 {2,3,4,5}\{2,3,4,5\} 的第 33 种,故变为 22
    • 335533 种选择 {2,3,5}\{2,3,5\} 的第 33 种,故变为 22
    • 442222 种选择 {2,3}\{2,3\} 的第 11 种,故变为 00
    • 553311 种选择 {3}\{3\} 的第 11 种,故变为 00

    最后,排列 1,4,5,2,31,4,5,2,3 变成了 (02200)5,4,3,2,1(02200)_{5, 4, 3, 2, 1}

    (02200)5,4,3,2,1+(3)10(02200)_{5, 4, 3, 2, 1} + (3)_{10},根据每一位的进制进位:

    • 55 位是 11 进制的,进 33(02230)(02230)
    • 44 位位是 22 进制的,满 2211(02310)(02310)
    • 33 位是 33 进制的,满 3311(03010)(03010)
    • 22 位是 44 进制的,3<43<4,不进位,得 (03010)5,4,3,2,1(03010)_{5, 4, 3, 2, 1}

    我们将他变回火星数:

    • 1100 表示这位应选择 {1,2,3,4,5}\{1,2,3,4,5\} 的第 11 种,即 11
    • 2233 表示这位应选择 {2,3,4,5}\{2,3,4,5\} 的第 44 种(11 被选过了),即 55
    • 3300 表示这位应选择 {2,3,4}\{2,3,4\} 的第 11 种,即 22
    • 4411 表示这位应选择 {3,4}\{3,4\} 的第 22 种,即 44
    • 5500 表示这位应选择 {3}\{3\} 的第 11 种,即 33

    其实这也叫康托展开。

    最后写出代码:

    #include<bits/stdc++.h>
    using namespace std;
    int n, m, a[10005];
    bool used[10005] = {0};
    int main()
    {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
            int x = a[i];
            for (int j = 1; j <= a[i]; j++)
                x -= used[j];
            used[a[i]] = 1;
            a[i] = x - 1;
        }
        a[n] += m;
        for (int i = n; i >= 1; i--)
        {
            a[i - 1] += a[i] / (n - i + 1);
            a[i] %= n - i + 1;
        }
        memset(used, 0, sizeof(used));
        for (int i = 1; i <= n; i++)
        {
            for (int j = 0;j <= a[i]; j++)
                if (used[j])
                    a[i]++;
            printf("%d ", a[i] + 1);
            used[a[i]] = 1;
        }
        printf("\n");
        return 0;
    }
    
    • 1

    [普及][NOIP2004 普及组] 火星人

    信息

    ID
    1706
    时间
    1000ms
    内存
    256MiB
    难度
    2
    标签
    递交数
    102
    已通过
    66
    上传者