Min Max Sort
简化题意
- 给定一个长度为
n 的排列p 。 - 你可以进行若干次(可以
0 次)下面操作: -
- 首先选择两个不同的元素,记为
x, y ,并将它们在p 中删除。 - 在
p 的开头插入\min \{x, y\} ,使其成为p 中第一个元素。 - 在
p 的最后插入\max \{x, y\} ,使其成为p 中最后一个元素。
- 首先选择两个不同的元素,记为
- 你的目标是通过最少的操作次数使得
p 变成升序的,即让p = [1, 2, 3, \ldots, n] 。 - 求这个最少的操作次数。
解题思路
观察到最后
并且容易证明选择重复的数一定不比不选重复的数更优。以此类推,如果有至少
例如样例的第四个字测试用例
但有时候答案不会取到
第一个例子是因为
记中间数
我们在找
时间复杂度:
AC Code
#include <cstdio>
#define N 200005
using namespace std;
int T, n, l, r, mid, a[N];
bool check (int x) // k = x 时中间的几个数是否已经排好序
{
int l = (n + 1 >> 1) - x + (~n & 1), r = (n + 1 >> 1) + x;
// 当 n 为奇数时,l = (n + 1) / 2 - x, r = (n + 1) / 2 + x
// 当 n 为偶数时,l = (n + 1) / 2 - x + 1, r = (n + 1) / 2 + x
for (int i = 1; i <= n; i ++) // 遍历数组
{
l += a[i] == l; // 如果 a[i] == l,则 l ++
}
return l > r; // 如果 l > r,则说明 l~r 这几个数都在数组中按顺序出现过,返回 true;否则返回 false
}
int main ()
{
scanf ("%d", &T);
while (T --)
{
scanf ("%d", &n);
for (int i = 1; i <= n; i ++)
{
scanf ("%d", &a[i]);
}
l = 0, r = n >> 1; // 确定二分域
while (l < r) // 开始二分
{
mid = l + r + 1 >> 1; // 中间数取 (l + r + 1) / 2,如果不加 1 可能会死循环
if (check (mid)) // 如果 k = mid 可以
{
l = mid;
}
else
{
r = mid - 1;
}
}
printf ("%d\n", (n >> 1) - l); // 答案为 n / 2 - k
}
return 0;
}