📜  门|门CS 2011 |第 34 题(1)

📅  最后修改于: 2023-12-03 15:12:47.308000             🧑  作者: Mango

题目介绍 - 门|门CS 2011 |第 34 题

题目描述

给定 $n$ 个人的身高和体重,两个人 $i$ 和 $j$ 可以比较其身高或体重大小,但是不能同时比较。

请定义一个算法,在 $O(nlogn)$ 时间内找出排名前 $k$ 的人。

输入格式

第一行包含两个整数 $n$ 和 $k$,分别表示人数和前 $k$ 名。

接下来 $n$ 行,每行包含两个数,分别表示第 $i$ 个人的身高和体重。

输出格式

输出一行,包含 $k$ 个用空格隔开的整数,为排名前 $k$ 名的人的编号。

其中编号从 $1$ 开始,按照身高排序输出,身高相同时按体重排序输出。

样例输入
4 2
1 2
4 4
2 3
3 1
样例输出
2 1
代码实现

本题可以使用类似于快排的分治思想来解决。

具体地,我们可以先将所有人按照身高排序,然后对于每个区间,可以随机选择一个人 $x$,将所有比 $x$ 高的人放到 $x$ 的左边,比 $x$ 矮的人放到 $x$ 的右边,然后根据 $x$ 的位置与 $k$ 的大小关系,分别在 $x$ 的左边或右边继续递归寻找前 $k$ 大的人。

这个算法的时间复杂度是 $O(nlogn)$,因为每次递归都会将一个区间缩小一半。

下面是使用 Python 3 语言实现的代码片段:

def partition(arr, lo, hi):
    pivot = arr[hi]
    i = lo
    for j in range(lo, hi):
        if arr[j] < pivot:
            arr[i], arr[j] = arr[j], arr[i]
            i += 1
    arr[i], arr[hi] = arr[hi], arr[i]
    return i

def kth_largest(arr, k):
    n = len(arr)
    lo, hi = 0, n - 1
    while True:
        pivot_idx = partition(arr, lo, hi)
        if pivot_idx == n - k:
            return arr[pivot_idx]
        elif pivot_idx < n - k:
            lo = pivot_idx + 1
        else:
            hi = pivot_idx - 1

def find_kth_persons(n, k, persons):
    sorted_by_height = [(h, w, i) for i, (h, w) in enumerate(persons)]
    sorted_by_height.sort()
    kth_height = kth_largest([h for h, _, _ in sorted_by_height], k)
    persons_above_kth_height = [(h, w, i) for h, w, i in sorted_by_height if h >= kth_height]
    persons_above_kth_height.sort(key=lambda x: (x[0] != kth_height, x[1]))
    kth_persons = [i + 1 for _, _, i in persons_above_kth_height[:k]]
    return kth_persons

其中,find_kth_persons 函数接受一个整数 n,一个整数 k,以及一个列表 persons,其中每个元素是一个两个整数元组 (height, weight),表示一个人的身高和体重。

该函数首先将所有人按照身高排序,然后找到第 $k$ 大的身高,接着将身高大于等于它的人按身高和体重排序,并返回排名前 $k$ 的人的编号列表。

上面给出的代码片段并非完整程序,可能需要一定的补充才能运行。