CF1638A

发布时间 2023-04-24 19:39:03作者: untitled0

考虑这样一种贪心策略:

按序号遍历 \(a\),如果 \(a_i=i\) 则继续,否则暴力向下找到一个 \(a_j=i\),暴力翻转区间 \([i,j]\),然后退出。

正确性证明:

当遍历到 \(a_i\) 时,\(a_1\)\(a_{i-1}\) 一定已经是不需要翻转就已经字典序最小,因为如果不是的话程序在这之前就已经退出了。

再考虑 \(a_i\)

  • 如果 \(a_i=i\),那么 \(a_1\)\(a_{i}\) 仍是最小字典序,继续遍历即可。
  • 如果 \(a_i\neq i\),我们进行暴力翻转后 \(a_i=i\)\(a_1\)\(a_{i}\) 变成了最小字典序。根据字典序性质,所有 \(a_1\)\(a_i\) 不是最小字典序的序列一定比 \(a_1\)\(a_i\) 是最小字典序的序列要小。因此这时得到的序列一定是进行一次操作可以得到最小的,退出即可。

code:

#include<bits/stdc++.h>
using namespace std;
int a[505];
int main(){
    int t;cin>>t;
    while(t--){
        int n;cin>>n;
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)
            if(a[i]!=i){
                int j=i+1;
                while(a[j]!=i)j++;//找到一个值为i的a[j]
                for(int k=i;k<j;k++,j--)swap(a[k],a[j]);//暴力翻转区间
                break;
            }
        for(int i=1;i<=n;i++)printf("%d ",a[i]);
        putchar(10);
    }
    return 0;
}