今天在浙大PAT里面看到了这一题,通过过滤挺低的,就像挑战下,毕竟只是Basic Level 嘛! 看到题目以为是用快速排序解决问题,后来只被题目忽悠了。废话不多说,直接上题目:
题目
著名的快速排序算法里有一个经典的划分过程:我们通常采用某种方法取一个元素作为主元,通过交换,把比主元小的元素放到它的左边,比主元大的元素放到它的右边。 给定划分后的N个互不相同的正整数的排列,请问有多少个元素可能是划分前选取的主元?
例如给定N = 5, 排列是1、3、2、4、5。则:
- 1的左边没有元素,右边的元素都比它大,所以它可能是主元;
- 尽管3的左边元素都比它小,但是它右边的2它小,所以它不能是主元;
- 尽管2的右边元素都比它大,但其左边的3比它大,所以它不能是主元;
- 类似原因,4和5都可能是主元。
因此,有3个元素可能是主元。
输入格式:
输入在第1行中给出一个正整数N(<= 105);
第2行是空格分隔的N个不同的正整数,每个数不超过109。
输出格式:
在第1行中输出有可能是主元的元素个数;在第2行中按递增顺序输出这些元素,其间以1个空格分隔,行末不得有多余空格。
输入样例:
5
1 3 2 4 5
输出样例:
3
1 4 5
代码
/*******************
"Programming Ability Test 1045"
Inversion pair problem:
find the pivot elements for quick sort
********************/
#include<iostream>
#include<cstdio>
using namespace std;
//fast io
const int sz = 1 << 20;
struct fastio{
char inbuf[sz];
char outbuf[sz];
fastio(){
setvbuf(stdin, inbuf, _IOFBF, sz);
setvbuf(stdout, outbuf, _IOFBF, sz);
}
}io;
//maximum array size
const int arr_size = 100010;
//store input numbers
int input_arr[arr_size];
//for marking the inversed elements which can not be the pivot elements
int result_arr[arr_size];
//temporary array for merge sort
int temp_sort[arr_size];
//store rest elements after marking all inversed elements
int temp_result[arr_size];
//size is the input array size, result_arr_size is the size of output elements
int size(0),result_arr_size(0);
//merge sort
void MergeSort(int lo, int hi);
int main(){
//redirecting IO
#define _OJ_
#ifndef _OJ_
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
scanf("%d", &size);
getchar();
int n;
for(int i =0; i < size; ++i){
scanf("%d", &n);
input_arr[i] = n;
result_arr[i] = n;
}
MergeSort(0, size-1);
int i, j;
//elements of inversion pairs are marked as 0 in merge sort
//and put non-zero elements into temp_result to put out
for(i = 0, j = 0; i < size; ++i)
if(result_arr[i] != 0){
temp_result[j++] = result_arr[i];
++result_arr_size;
}
printf("%d\n",result_arr_size);
//notice the special conditon that all element are inversed and marked as 0
if(result_arr_size != 0){
for(j = 0; j < result_arr_size - 1; ++j)
printf("%d ", temp_result[j]);
printf("%d\n", temp_result[j]);
}else
printf("\n");
return 0;
}
//merge sort and fin inversion pair
void Merge(int lo, int mid, int hi){
int i = lo, j = mid + 1;
int k = 0;
while(i <= mid && j <= hi){
if(input_arr[i] < input_arr[j])
temp_sort[k++] = input_arr[i++];
else{
//input_arr[i] and input_arr[j] is an inversion pair, mark the element in result_arr
result_arr[i] = 0;
result_arr[j] = 0;
temp_sort[k++] = input_arr[j++];
}
}
while(i <= mid){
//the rest left elements are inversed to the right elements so they should be marked
result_arr[i] = 0;
temp_sort[k++] = input_arr[i++];
}
while(j <= hi)
temp_sort[k++] = input_arr[j++];
for(k = 0, i = lo; i <= hi; ++i,++k)
input_arr[i] = temp_sort[k];
}
void MergeSort(int lo, int hi){
int mid;
if(lo < hi){
mid = (lo + hi) >> 1;
MergeSort(lo, mid);
MergeSort(mid+1, hi);
Merge(lo, mid, hi);
}
}
其实稍微分析下题目就能看出来这是一道打着快速排序的幌子解决逆序对的问题,没错,其实就是解决逆序对的问题,把所以逆序对从原数组中去除的结果就是本题的答案了。
最大的问题就是算法复杂度了,用普通的循环嵌套方法我试了下,最多通过前三个case,后面的几个case就直接超时了。
这一题和THU数据结构的“灯塔(LightHouse)”那一题的解题思想相似(抱歉,找了一下,发现那题我忘记整理博客了 ㄟ( ▔, ▔ )ㄏ )。
关于逆序对首先考虑的是用归并排序解决。复杂度控制在(nlogn)是极好的。对于顺序的两个数,左边的会比右边的小,一旦发生左边比右边的数字大的情况那这两个数就是一个逆序对了,在result_arr
中标记处这两个数(其实就是把它们置0),最后所有逆序对的数字都被置零了,剩下的元素自然也就是有序的了,所以可以直接输出了。
这里有一个地方需要注意:可能最后可以诸位快速排序的主元的元素个数为0,就是说原数组的所有元素都和其他元素构成逆序对。这时候的输出应该为空。我就是在这个问题上耽误了好久,恨自己恨得咬牙切齿。。。