CF1455D 序列和交换
1 CF1455D 序列和交换
2 题目概要:
给定一个序列 \(𝑎\) 由 \(𝑛\) 个整数 \(𝑎1,𝑎2,...,𝑎𝑛\) 和一个整数 \(𝑥\) 组成。对 \(𝑎\) 进行升序排序, 如果 \(𝑎_1≤𝑎_2≤𝑎_3≤⋯≤𝑎_𝑛\) 成立,则称之为有序。
要对序列进行排序,可以执行任意次数的以下操作(也有可能零次):选择一个整数 \(𝑖\) 使 \(1≤𝑖≤𝑛\) 同时 \(𝑎_𝑖>𝑥\),交换 \(𝑎_𝑖\) 和 \(𝑥\) 的值。
例如,如果 \(𝑎=[0,2,3,5,4]\), \(x=1\), 下列操作顺序是可能的:
-
选择 \(𝑖=2\)(有可能因为 \(𝑎_2>𝑥\)),所以 \(𝑎=[0,1,3,5,4]\), \(𝑥=2\);
-
选择 \(𝑖=3\)(有可能因为 \(𝑎_3>𝑥\)),所以 \(𝑎=[0,1,2,5,4]\), \(𝑥=3\);
-
选择 \(𝑖=4\)(有可能因为 \(𝑎_4>𝑥\)),所以 \(𝑎=[0,1,2,3,4]\), \(𝑥=5\)。
计算对 \(𝑎\) 进行排序需要的最少操作次数或者输出不可能。
3 解题思路
这道题是一道明显的数学题。分析性质,发现:如果序列中的某个数比 \(x\) 小且比它前面的那个数小(即无序),那么无论我们如何交换,都无法将该序列改为递增序列。由于与 \(x\) 交换的数必须比 \(x\) 要大,所以 \(x\) 在每次交换完成后必将变大。所以当前 \(k\) 个数已经变成有序数列且前 \(k\) 个数都小于等于 \(x\),后面的数都大于 \(x\) 时,我们必须将第 \(k+1\) 个数与 \(x\) 交换。否则,如果我们先跟后面的数交换,那么后面的数必定小于第 \(k+1\) 个数。根据性质,该数列无法成为递增序列。如果不满足前 \(k\) 个数为有序数列,前 \(k\) 个数都比 \(x\) 小,后面的数都大于 \(x\) 其中的任意一条,那么也无法成为递增序列。
由此,我们可以推出本题做法:每一次操作,将\(x\)与当前的第一个数交换,答案 \(+1\),然后判断小于等于交换后的 \(x\) 的数组成的数列是否递增。若不是递增,则无法变成递增数列。若为递增,则抹去这些数。每次操作结束后都判断整个序列是否变成了递增序列,如果变成了递增序列,就输出答案。
4 代码实现
代码(空格警告):
#include <iostream>
using namespace std;
const int N = 505;
int T, n, x, head, flag, cnt;
int a[N];
void update()
{
while (head <= n)
{
if (a[head] > x) break;
if (a[head] < a[head-1])
{
flag = 1;
break;
}
head++;
}
}
int main()
{
cin >> T;
while (T--)
{
cin >> n >> x;
for (int i = 1; i <= n; i++) cin >> a[i];
head = 1, flag = 1, cnt = 0;
for (int i = 1; i <= n; i++) if (a[i] < a[i-1]) flag = 0;
if (flag)
{
cout << 0 << endl;
continue;
}
update();
if (flag)
{
cout << -1 << endl;
continue;
}
while (head <= n)
{
swap(a[head], x);
update();
if (flag)
{
cout << -1 << endl;
break;
}
cnt++;
flag = 1;
for (int i = 1; i <= n; i++) if (a[i] < a[i-1]) flag = 0;
if (flag)
{
cout << cnt << endl;
break;
}
}
}
return 0;
}