AcWing寒假每日一题——Day20火星人

火星人

人类终于登上了火星的土地并且见到了神秘的火星人。

人类和火星人都无法理解对方的语言,但是我们的科学家发明了一种用数字交流的方法。

这种交流方法是这样的,首先,火星人把一个非常大的数字告诉人类科学家,科学家破解这个数字的含义后,再把一个很小的数字加到这个大数上面,把结果告诉火星人,作为人类的回答。

火星人用一种非常简单的方式来表示数字——掰手指。

火星人只有一只手,但这只手上有成千上万的手指,这些手指排成一列,分别编号为 1 , 2 , 3 … … 。 1,2,3……。 123

火星人的任意两根手指都能随意交换位置,他们就是通过这方法计数的。

一个火星人用一个人类的手演示了如何用手指计数。

如果把五根手指——拇指、食指、中指、无名指和小指分别编号为 1 , 2 , 3 , 4 和 5 1,2,3,4和5 12345,当它们按正常顺序排列时,形成了 5 位 数 12345 5位数12345 512345,当你交换无名指和小指的位置时,会形成 5 位 数 12354 5位数12354 512354,当你把五个手指的顺序完全颠倒时,会形成 54321 54321 54321,在所有能够形成的 120 个 5 位 数 中 120个5位数中 1205 12345 12345 12345最小,它表示 1 ; 12354 第 二 小 , 它 表 示 2 ; 54321 最 大 , 它 表 示 120 。 1;12354第二小,它表示2;54321最大,它表示120。 112354254321120

下表展示了只有 3 3 3根手指时能够形成的 6 个 3 6个3 63位数和它们代表的数字:

三位数 123 132 213 231 312 321

代表的数字 1 2 3 4 5 6

现在你有幸成为了第一个和火星人交流的地球人。

一个火星人会让你看他的手指,科学家会告诉你要加上去的很小的数。

你的任务是,把火星人用手指表示的数与科学家告诉你的数相加,并根据相加的结果改变火星人手指的排列顺序。

输入数据保证这个结果不会超出火星人手指能表示的范围。

输入格式
输入包括三行,第一行有一个正整数 N N N,表示火星人手指的数目。

第二行是一个正整数 M M M,表示要加上去的小整数。

下一行是 1 1 1 N N N N N N个整数的一个排列,用空格隔开,表示火星人手指的排列顺序。

输出格式
输出只有一行,这一行含有 N N N个整数,表示改变后的火星人手指的排列顺序。

每两个相邻的数中间用一个空格分开,不能有多余的空格。

数据范围
1 ≤ N ≤ 10000 , 1≤N≤10000, 1N10000,
1 ≤ M ≤ 100 1≤M≤100 1M100
输入样例:

5
3
1 2 3 4 5

输出样例:

1 2 4 5 3

分析: 题目的意思就是让我们求 n n n个数的下 m m m个排列, S T L STL STL n e x t p e r m u t a t i o n nextpermutation nextpermutation函数,所以直接调用即可。

代码:

#include<bits/stdc++.h>
using namespace std;
int m,n,a[100010];
int main(){
    
    
    cin>>n>>m;
    for(int i=0;i<n;i++) cin>>a[i];
    while(m--) next_permutation(a,a+n);
    for(int i=0;i<n;i++) cout<<a[i]<<" ";
}

如果要我们手写这个函数,该如何实现?
分析:
从后往前,找出这个序列第一个不递增的数(设位置为 k − 1 k-1 k1)。
即满足 a [ k ] < a [ k − 1 ] 且 ∀ x ∈ [ k , n ] , 都 有 a [ x ] > a [ x − 1 ] a[k]<a[k-1]且\forall x\in[k,n],都有a[x]>a[x-1] a[k]<a[k1]x[k,n],a[x]>a[x1]
就是如果一个子序列从后往前都是递增的,那么就无法在继续进行下一个排列。
当找到第一个不递增的数时,就再次从前往后找到第一个(设位置为m)
满足 ∀ x ∈ ( m , n ] , a [ m ] > a [ x ] , ∀ y ∈ ( k , m ) , a [ m ] < a [ y ] \forall x\in (m,n],a[m]>a[x],\forall y\in(k,m),a[m]<a[y] x(m,n],a[m]>a[x],y(k,m),a[m]<a[y]的数
之后交换两个元素的值。然后对区间 [ k , n ] [k,n] [k,n]排序,这里根据性质,只需要逆序一下就可以了。

代码:

#include<bits/stdc++.h>
using namespace std;
int a[100010];
int main(){
    
    
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i];
    while(m--){
    
    
        int k=n;
        while(a[k]<a[k-1]) k--;
        int t=k;
        while(a[t+1]>a[k-1]) t++;
        swap(a[t],a[k-1]);
        reverse(a+k,a+n+1);
    }
    for(int i=1;i<=n;i++) cout<<a[i]<<" ";
}