暴力枚举 构造算法 贪心 *1400

本文介绍了如何通过单调栈来寻找字典序最大的排列,通过翻转和区间交换操作,逐步将比后面元素小的数移到最右侧,最后输出排列的顺序。
摘要由CSDN通过智能技术生成

https://codeforces.com/contest/1833/problem/D

选择一个区间 [l,r](1≤lrn),将这个区间内的数翻转。 将原来排列中 [r+1,n] 和 [1,−1] 的位置互换。即将排列分为两段,前面一段是 [r+1,n],后面一段是 [1,−1]

请你输出通过上述操作能够得到的字典序最大的排列。

解题思路

首先考虑如何得到字典序最大的排列。我们很容易想到,对于每个位置 i,我们应该把它和后面最大的比它大的数交换,这样可以保证得到的排列字典序最大。

那么问题就转化成了如何找到后面最大的比当前位置大的数。可以使用单调栈实现,维护一个单调递减的栈,遇到比栈顶元素大的数就弹出栈顶并记录答案。

接下来考虑如何利用上述操作将排列变成字典序最大的。

首先,我们可以发现对于一个区间 [l,r],如果将其翻转,再把前后两部分交换,相当于把 pi 和 pr 交换了位置,同时把 +1…−1pl+1…r−1 翻转。

我们可以发现,如果要让某个 pi 能够到达最右侧的位置,那么在翻转后一定要满足 i∈[l,r]。因此我们考虑对于每个 i,找到最靠右的 j,使得 i∈[j,n]

找到 j 后,我们可以将 [j,i] 这个区间翻转,然后将 [i+1,n] 和 [1,j−1] 两个区间交换。这样就可以保证操作后 i 会被移动到最右侧,且不会影响到其他元素的位置关系。

因为我们要求字典序最大,所以每次找到最靠右的 j 后,应该让 pj 和 pi 交换位置。这样就可以一步步将所有比后面元素小的元素移到最右侧,最终得到字典序最大的排列。

#include <iostream>
#include <vector>
#include <stack>

using namespace std;

vector<int> getLexicographicallyMaxPermutation(vector<int>& p) {
    int n = p.size();
    vector<int> result(n); // 存储结果的数组

    stack<int> st; // 单调递减栈,存储后面最大的比当前位置大的数
    for (int i = n - 1; i >= 0; i--) {
        while (!st.empty() && st.top() < p[i]) {
            st.pop();
        }
        if (!st.empty()) { // 找到了比当前位置大的数
            result[i] = st.top();
        } else {
            result[i] = 0; // 没有找到,则置为0
        }
        st.push(p[i]);
    }

    return result;
}

int main() {
    int t;
    cin >> t; // 读取测试用例数量

    while (t--) {
        int n;
        cin >> n; // 读取排列长度

        vector<int> p(n);
        for (int i = 0; i < n; i++) {
            cin >> p[i]; // 读取排列元素
        }

        vector<int> result = getLexicographicallyMaxPermutation(p);
        for (int i = 0; i < n; i++) {
            cout << result[i] << " "; // 输出结果
        }
        cout << endl;
    }

    return 0;
}

有比后面元素小的元素移到最右侧,最终得到字典序最大的排列

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值