P1088 [NOIP2004 普及组] 火星人

// Problem: P1088 [NOIP2004 普及组] 火星人
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1088
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// User: Pannnn

#include <bits/stdc++.h>

using namespace std;
/*
    下一个排列总是比当前排列要大,除非该排列已经是最大的排列。
    希望找到一种方法,能够找到一个大于当前序列的新序列,且变大的幅度尽可能小。
    
    需要将左边的一个较小数与右边的一个较大数交换,以能够让当前排列变大,从而得到下一个排列。
    同时要让这个较小数尽量靠右,而较大数尽可能小,当交换完成后,
    较大数右边的数按照升序重新排列,可以保证新排列大于原来排列的情况下,使变大的幅度尽可能小。
    
    以4 5 2 6 3 1为例
    能找到的符合条件的一对较少数与较大数的组合为2 3,满足较小数尽可能靠右且较大数尽可能小
    完成交换后,排列变为4 5 3 6 2 1,此时重排原来较小数位置右边的序列,序列变为4 5 3 1 2 6
    
    描述算法:
    对于长度为n的排列a:
    首先从后向前查找第一个顺序对(i, i + 1),满足a[i] < a[i + 1],这样较小数为a[i],此时[i + 1, n)必然为降序
    如果找到了顺序对,那么在区间[i + 1, n)中从后向前查找第一个元素j满足a[i] < a[j],这样较大数为a[j]
    交换a[i]与a[j],此时可以证明区间[i + 1, n)必为降序,可以直接反转使其变为升序
*/
void nextPermutation(vector<int> &nums) {
    int i = nums.size() - 2;
    while (i >= 0 && nums[i] >= nums[i + 1]) {
        --i;
    }
    if (i >= 0) {
        int j = nums.size() - 1;
        while (j >= 0 && nums[i] >= nums[j]) {
            --j;
        }
        swap(nums[i], nums[j]);
    }
    reverse(nums.begin() + i + 1, nums.end());
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int n, m;
    cin >> n >> m;
    vector<int> info(n);
    for (int i = 0; i < n; ++i) {
        cin >> info[i];
    }
    
    for (int i = 0; i < m; ++i) {
        nextPermutation(info);
    }
    for (int i = 0; i < n; ++i) {
        cout << info[i] << " ";
    }
    cout << endl;
    return 0;
}
posted @ 2022-02-08 17:46  Pannnn  阅读(187)  评论(0)    收藏  举报
-->