考虑这样一种贪心策略:
按序号遍历 \(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;
}