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;
}
posted @ 2020-12-31 13:49  Withinlover  阅读(73)  评论(0编辑  收藏  举报