完美洗牌问题(2)
题目描述
给定一个数组arr,请将数组调整为依次相邻的数字,总是先<=、再>=的关系,并交替下去。比如数组中有五个数字,调整成[a,b,c,d,e],使之满足a<=b>=c<=d>=e。
输入描述:
输入包含两行,第一行一个整数 n ( 1 ≤ n ≤ 1 0 5 ) n(1 \leq n \leq 10^5) n(1≤n≤105),代表数组的长度,接下来一行n个整数,代表数组 a r r ( 1 ≤ a r r i ≤ 1 0 9 ) arr(1 \leq arr_i \leq 10^9) arr(1≤arri≤109)。
输出描述:
输出一行,代表调整后的数组。
示例1
输入
6
1 2 3 4 5 6
输出
1 3 2 5 4 6
示例2
输入
3
1 2 3
输出
1 3 2
备注:
时间复杂度 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n),空间复杂度 O ( 1 ) O(1) O(1)。
题解:
构造题,肯定有多种解法,这里提供两种构造方法:
法一:
参考 完美洗牌问题(1) ,对数组按照从大到小排序,然后分奇偶讨论:
- 若元素个数为奇数,则对 a[1…n-1] 进行完美洗牌操作
- 若元素个数为偶数,则对 a[1…n] 进行完美洗牌操作
法一代码:
#include <cstdio>
#include <functional>
#include <algorithm>
using namespace std;
const int N = 100001;
const int INF = 1e9 + 10;
int n;
int a[N];
int main(void) {
scanf("%d", &n);
for ( int i = 1; i <= n; ++i ) scanf("%d", a + i);
sort( a + 1, a + n + 1, greater<int>() );
int m;
if ( n & 1 ) m = n - 1;
else m = n;
for ( int i = 1; i <= m; ++i ) {
if ( a[i] > 0 ) {
int pre = a[i] - INF;
int nxt = (i << 1) % (m + 1);
while ( nxt != i ) {
int tmp = a[nxt] - INF;
a[nxt] = pre;
pre = tmp;
nxt = (nxt << 1) % (m + 1);
}
a[nxt] = pre;
}
}
if ( n & 1 ) a[n] -= INF;
for ( int i = 1; i <= n; ++i )
printf("%d ", a[i] + INF);
return 0;
}
法二:
这种方法更简单,对数组从小到大排序,将偶数位数字与下一位奇数位数字进行交换即可。
法二代码:
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 100001;
int n;
int a[N];
int main(void) {
scanf("%d", &n);
for ( int i = 1; i <= n; ++i ) scanf("%d", a + i);
sort( a + 1, a + n + 1 );
for ( int i = 2; i < n; i += 2 )
swap( a[i], a[i + 1] );
for ( int i = 1; i <= n; ++i )
printf("%d ", a[i]);
return 0;
}