ACM刷题之路(十九)二分+尺取 2019暑期集训 HDU6231 K-th Number

简介: ACM刷题之路(十九)二分+尺取 2019暑期集训 HDU6231 K-th Number

题目链接:传送门

题意:输入N,M,K,第二行输入N个数字,假设第二行是A数组,那么从A数组中所有连续且长度大于等于M的子区间 的 第M大的数字,放入B数组(可重复),最后输出B数组中第K大的数字。

样例:

5 3 2

2 3 1 5 4

子区间 2 3 1、3 1 5、 1 5 4、2 3 1 5、 3 1 5 4、2 3 1 5 4的第3大的数分别是1 1 1 2 3 3,所以第二大的数字是3 输出即可

最暴力的是一一取出长度大于M的区间,再组成B数组,然后求第K大的数,那肯定超时。

这里用到二分加尺取的方法

第一步:B数组的元素肯定来自于A数组,并且来自于A数组某个区间第M大的数字

第二步:B数组元素最小是A数组的最小值,最大值是A数组的最大值,即可以二分

第三步:s=min(A),e=max(A);while(s<e){} 开启二分  对于s和e的 mid值 运用尺取法进行判断和答案k比较

第四步:对于每一个mid,需要找出有多少个区间满足区间内mid至少排第M位

比如样例 2 3 1 5 4      第一次二分mid=(1+5)/2=3, 那么3在[2]中排第0位

(假设排序规则是有x个数大于等于它,那么这个数就排第几位)

同理3在[2,3]中排第一位,   3在[2,3,1]中第一位, 3在[2,3,1,5]中排第二位,在[2,3,1,5,4]中排第三位......

所以可以得出区间[2,3,1,5,4]满足3>=3,即这个区间的第M位大于等于枚举的mid,即B数组中排mid的右边或者重合

题目要求输出B数组中第k大的数字,我们只要满足对于每一个二分到的mid值,其右边的数(B数组中)小于k即可。

如果大于,说明枚举到的mid值在B数组中靠左了,让s=mid+1,否则让e=mid;

PS:有一个可以优化的地方  就 是   ans += n - e;

设想[2 3 1 5 4]中3排第三位 ,如果在该区间的右边再加上比3小的数字,3还是排第三位。

如果加上比3大的数字,那么3可能会排第Y位,这个Y必定大于等于三,即加数字后的区间的第M位在B数组中一定在mid的右边。

有点烧脑,理解了就很简单了,代码如下:

1. #include<iostream>
2. #include<algorithm>
3. #include<cstring>
4. #include<vector>
5. using namespace std;
6. int a[100010];
7. vector<int>v;
8. int n, m;
9. long long k;
10. long long fx(int x) {
11.   long long ans = 0;
12.   int s = 0, e = -1, no = 0;//no比x大于等于的数字有多少
13.   while (e < n) {
14.     if (no < m) {
15.       e++;
16.       if (a[e] >= x) {
17.         no++;
18.       }
19.     }
20.     else {
21.       if (no == m) {
22.         ans += n - e;
23.       }if (a[s] >= x) no--;
24.       s++;
25.     }
26.   }
27.   return ans;
28. }
29. int main()
30. {
31.   int t;
32.   scanf_s("%d", &t);
33.   while (t--) {
34.     scanf_s("%d%d%lld", &n, &m, &k);
35.     for (int i = 0; i < n; i++) {
36.       scanf_s("%d", &a[i]);
37.       v.push_back(a[i]);
38.     }
39.     sort(v.begin(), v.end());
40.     int len = unique(v.begin(), v.end()) - v.begin();
41.     int s = 0, e = len, ans = -1;
42.     while (s < e) {
43.       int mid = (s + e) / 2;
44.       if (fx(v[mid])>=k) {
45.         s = mid + 1;
46.         ans = v[mid];
47.       }
48.       else {
49.         e = mid;
50.       }
51.     }
52.     printf("%d\n", ans);
53.   }
54.   return 0;
55. }

 


相关文章
|
7月前
K-th Number(尺取)
K-th Number(尺取)
57 0
|
算法
Leetcode 313. Super Ugly Number
题目翻译成中文是『超级丑数』,啥叫丑数?丑数就是素因子只有2,3,5的数,7 14 21不是丑数,因为他们都有7这个素数。 这里的超级丑数只是对丑数的一个扩展,超级丑数的素因子不再仅限于2 3 5,而是由题目给定一个素数数组。与朴素丑数算法相比,只是将素因子变了而已,解法还是和朴素丑数一致的。
105 1
|
6月前
|
存储 SQL 算法
LeetCode 题目 65:有效数字(Valid Number)【python】
LeetCode 题目 65:有效数字(Valid Number)【python】
|
7月前
|
存储 算法
【LeetCode力扣】单调栈解决Next Greater Number(下一个更大值)问题
【LeetCode力扣】单调栈解决Next Greater Number(下一个更大值)问题
53 0
|
存储
Leetcode Single Number II (面试题推荐)
给你一个整数数组,每个元素出现了三次,但只有一个元素出现了一次,让你找出这个数,要求线性的时间复杂度,不使用额外空间。
42 0
|
算法
LeetCode 414. Third Maximum Number
给定一个非空数组,返回此数组中第三大的数。如果不存在,则返回数组中最大的数。要求算法时间复杂度必须是O(n)。
97 0
LeetCode 414. Third Maximum Number
|
存储
LeetCode 313. Super Ugly Number
编写一段程序来查找第 n 个超级丑数。 超级丑数是指其所有质因数都是长度为 k 的质数列表 primes 中的正整数。
107 0
LeetCode 313. Super Ugly Number
|
算法
LeetCode 306. Additive Number
累加数是一个字符串,组成它的数字可以形成累加序列。 一个有效的累加序列必须至少包含 3 个数。除了最开始的两个数以外,字符串中的其他数都等于它之前两个数相加的和。 给定一个只包含数字 '0'-'9' 的字符串,编写一个算法来判断给定输入是否是累加数。 说明: 累加序列里的数不会以 0 开头,所以不会出现 1, 2, 03 或者 1, 02, 3 的情况。
131 0
LeetCode 306. Additive Number