2020 算法上机赛 C1 - J 寻找周思明
题目描述
此题只能使用C语言提交
前苏联时期生产出的最强作品,失踪已久的实验体AD3,美国军方最近突然发现其与一个名叫周思明的普通高中生高度相似,因此决定派你前往调查。
你手上有一台机器,可以显示一个人的战斗力,通过前期调查,你得到了包含目标在内的 \(n\) 个人的战斗力。
此时你得到了一个重要信息,周思明的战斗力在这 \(n\) 个的中排名第 \(k\) 大。
现在你需要向上级汇报,周思明的战斗力是多少。
输入
第一行,包含一个正整数,为数据的组数 \(T\)。
接下来包含 \(T\) 组数据,每组数据包含两行:
第一行有两个正整数 \(n,k\) 分别对应人数和周思明的战斗力排名。(保证 \(n\geq k\) )
第二行包含nn个正整数 \(a1,a2,...,an\) ,分别为这 \(n\) 个人的战斗力。
输出
对于每组数据,输出一行,包含一个正整数,为周思明的战斗力。
输入样例
2
6 5
1 4 2 8 5 7
10 9
3 1 4 1 5 9 2 6 5 3
输出样例
2
1
数据范围与约定
对于所有测试点,都有 \(0<ai\leq1e8\) ,每个测试点的分值都为10分。
测试点1~5,每个测试点内满足 \(∑n\leq1e5\) ∑
测试点6~8,每个测试点内满足 \(∑n\leq1e7\),且数据内的所有aiai为随机生成,即你可以认为你的算法不会遇到最坏情况
测试点9,10,每个测试点内满足 \(∑n\leq1e7\) ,数据为助教精心构造,你的算法应当能够处理可能的极端数据
提示
能否借鉴快速排序的思想?
如何利用随机应对魔鬼(X)助教出的hack数据?
题目背景:漫画《惊爆校园》,作者:L.M.,已腰斩。
做法
每一轮快排,都会确定一个数字在最终排序中的位置
如果只想找第 \(k\) 大的元素,可以只递归一半。这样的复杂度是 \(O(n)\) 的
- 助教卡掉了选取区间中点,选取做左侧作为标准值的快排
- 助教卡掉了没有聚拢重复元素的快排
所以需要写随机化
另外:数据并不强,\(O(n\log n)\) 的排序算法也可以通过本题
排序法
#include <stdio.h>
#include <stdlib.h>
int cmp(const void *a, const void *b) {
int A = *(int*)a;
int B = *(int*)b;
return A < B;
}
int n, k, a[10001000];
void work() {
scanf("%d%d", &n, &k);
for(int i = 0;i < n; ++i) scanf("%d", a + i);
qsort(a, n, sizeof(int), cmp);
printf("%d\n", a[k - 1]);
}
int main() {
int T; scanf("%d", &T);
while(T--)
work();
return 0;
}
正解
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <ctype.h>
#define N 1000100
int read() {
int q=0,w=1;char c=getchar();
while(!isdigit(c)){if(c=='-')w=-1;c=getchar();}
while(isdigit(c))q=q*10+c-'0',c=getchar();
return w*q;
}
int n, k, a[N], T;
void swap(int *x, int *y) {
int t = *x;
*x = *y;
*y = t;
}
int solve(int l,int r) {
int index = l + (rand() % (r - l + 1));
swap(&a[l], &a[index]);
int i = l, j = r, tmp = a[l];
while(i < j) {
while(i < j && a[j] < tmp) j--;
while(i < j && a[i] >= tmp) i++;
swap(&a[i], &a[i == j ? l : j]);
}
while(a[i] == tmp) i++;
while(a[j] == tmp) j--;
if(j < k && k < i) return tmp;
if(i <= k) return solve(i, r);
if(j >= k) return solve(l, j);
}
int main() {
T = read();
while(T--) {
n = read(); k = read();
for(int i = 1;i <= n; ++i)
a[i] = read();
printf("%d\n", solve(1, n));
}
return 0;
}