P16316 [ICPC 2023 Jinan R] 奇怪的排序
好久没搬题解了,好像一直在写水题……
P16316 [ICPC 2023 Jinan R] 奇怪的排序
题目描述
:::epigraph[Stanley P. Y. Fung. Is this the simplest (and most surprising) sorting algorithm ever? arXiv:2110.01111]
我们给出了一个极其简单的排序算法。它看上去显然是错的,但我们证明了它事实上是对的。
:::
在学习了 2021 国际大学生程序设计竞赛亚洲区域赛南京站的《Paimon Sorting》一题中奇怪的排序算法后,小青鱼想到了如下的一个问题。
给定序列 \(a_1, a_2, \cdots, a_n\) 表示一个 \(n\) 的排列,您需要将该排列按升序排序,为此可以执行以下操作至多 \(\lfloor \frac{n}{2} \rfloor\) 次:选择两个下标 \(l\) 和 \(r\) 满足 \(1 \le l < r \le n\) 以及 \(a_l > a_r\),将 \(a_l, a_{l + 1}, \cdots, a_r\) 按升序进行排序。
请回忆:一个 \(n\) 的排列是一个长度为 \(n\) 的序列,每个从 \(1\) 到 \(n\)(含两端)的整数在其中都恰好出现一次。另外,\(\lfloor x \rfloor\) 表示小于等于 \(x\) 的最大的整数。
输入格式
有多组测试数据。第一行输入一个整数 \(T\) 表示测试数据组数,对于每组测试数据:
第一行输入一个整数 \(n\)(\(1 \le n \le 100\))表示排列的长度。
第二行输入 \(n\) 个不同的整数 \(a_1, a_2, \cdots, a_n\)(\(1 \le a_i \le n\))表示给定的排列。
保证所有数据 \(n\) 之和不超过 \(10^4\)。
输出格式
对于每组数据,首先输出一行一个整数 \(k\)(\(0 \le k \le \lfloor \frac{n}{2} \rfloor\))表示您执行的操作次数。接下来输出 \(k\) 行,其中第 \(i\) 行输出两个由单个空格分隔的整数 \(l_i\) 和 \(r_i\),表示您为第 \(i\) 次操作选择的两个下标。
可以证明答案总是存在。如果有多种合法答案,您可以输出任意一种。
输入输出样例 #1
输入 #1
3
6
2 3 4 6 5 1
5
1 2 3 4 5
3
2 3 1
输出 #1
2
3 6
1 3
0
1
1 3
说明/提示
对于第一组样例数据,在第 \(1\) 次操作后排列变为 \(\{2, 3, 1, 4, 5, 6\}\),在第 \(2\) 次操作后排列变为 \(\{1, 2, 3, 4, 5, 6\}\),此时排列是升序的。
题解:
题意:
对于一个排列,可以取一个满足端点是逆序对的区间进行排序,要求在 \(\lfloor \frac{n}{2} \rfloor\) 次上述操作以内使得该排列升序。
思路:
考虑贪心。
先感性理解一下,从前往后找到不属于这个位置(即 \(a_i\ne i\))的数,因为它一定需要改变。然后从后往前找第一个小于它的数,这样排序的区间最长,可以尽可能消除逆序对的个数,然后排序完往下走直到完成。
证明:
可以先看一下第一位的证明:
我们假设第一个数不是 \(1\),那么从后往前找,第一个小于 \(a_1\) 的数如果是 \(1\) 的话那么排完序之后 \(1\) 一定到第一个,满足条件;如果第一个小于 \(a_1\) 的数 \(a_j\) 不是 \(1\),就证明 \(1\) 一定在 \([1,j]\) 之间,因此排序后 \(1\) 一定可以归位。
如果你还是没有思路:
由上面的证明,我们可以知道,每次对于第 \(i\) 位的操作一定会把 \(i\) 归位,所以第 \(i\) 位前面保证有序。
因此,就可以得到需要操作的 \(a_i\ne i\) 可以转化为 \(a_i>i\),也就是说 \(a_i\ge i+1\),同时由上面的证明扩展:如果 \(a_i>i+1\),那么 \([a_i,a_{a_i}]\) 内的所有数一定都会被这次操作归位。这是因为从后往前,外面的都 \(>a_i\),所以 \(\in [i,a_i]\) 的数一定在所选区间内。
那么怎么证明操作次数一定 \(\le\lfloor \frac{n}{2} \rfloor\) 呢?这就很简单了,区间的长度最小为 \(2\),所以每次操作至少使得两个数归位,操作次数一定 \(\le\lfloor \frac{n}{2} \rfloor\)。
这样的话,扫描逆序对是 \(O(n^2)\),排序是 \(O(n\log n)\),总复杂度 \(O(n^3\log n)\),可以处理本题 \(100\) 的数据范围。
#include<bits/stdc++.h>
using namespace std;
long long t,n,m,a[10005],l[10005],r[10005];
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>t;
while(t--){
cin>>n;
int cnt=0;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)
if(a[i]-i){//找位置不对的
for(int j=n;j>i;j--)//从后往前
if(a[j]<a[i]){//找到最远逆序对
l[++cnt]=i;
r[cnt]=j;
sort(a+i,a+1+j);
break;
}
}
cout<<cnt<<"\n";
for(int i=1;i<=cnt;i++)cout<<l[i]<<" "<<r[i]<<"\n";
}
return 0;//好习惯
}

浙公网安备 33010602011771号