九度oj 题目1534:数组中第K小的数字

题目描述:

给定两个整型数组A和B。我们将A和B中的元素两两相加可以得到数组C。
譬如A为[1,2],B为[3,4].那么由A和B中的元素两两相加得到的数组C为[4,5,5,6]。
现在给你数组A和B,求由A和B两两相加得到的数组C中,第K小的数字。

输入:

输入可能包含多个测试案例。
对于每个测试案例,输入的第一行为三个整数m,n, k(1<=m,n<=100000, 1<= k <= n *m):n,m代表将要输入数组A和B的长度。
紧接着两行, 分别有m和n个数, 代表数组A和B中的元素。数组元素范围为[0,1e9]。

输出:

对应每个测试案例,
输出由A和B中元素两两相加得到的数组c中第K小的数字。

样例输入:
2 2 3
1 2
3 4
3 3 4
1 2 7
3 4 5
样例输出:
5
6

此题真的很难。这个题的难度在于数组的长度非常长长长长长长长长长长长长长长长长长长长长,我开始的代码是这样的:
 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <cstring>
 4 #include <string>
 5 #include <algorithm>
 6 #define MAX 100009
 7  
 8 long long A[MAX];
 9 long long B[MAX];
10 long long C[MAX];
11  
12 int cmp(const void *a, const void *b) {
13     long long at = *(long long *)a;
14     long long bt = *(long long *)b;
15     return (int)(at - bt);
16 }
17  
18 int main(int argc, char const *argv[])
19 {
20     int m, n, k;
21     while(scanf("%d %d %d",&m, &n, &k) != EOF) {
22         for(int i = 0; i < m; i++) {
23             scanf("%lld",&A[i]);
24         }
25         for(int i = 0; i < n; i++) {
26             scanf("%lld",&B[i]);
27         }
28         int t = 0;
29         for(int i = 0; i < m; i++) {
30             for(int j = 0; j < n; j++) {
31                 C[t] = A[i] + B[j];
32                 t++;
33             }
34         }
35          
36         qsort(C, t, sizeof(long long), cmp);
37         printf("%lld
", C[k-1]);
38     }
39     return 0;
40 }

结果就run time error了

之后才明白此题需要用二分搜索,现对A,B两个数组排序,可知道他们的最大值,最小值。那么所要求的第几小的数字必然在这两个值之间,我们此时采用二分搜索的办法去找到这个值。具体的做法是先求出中间值mid,在求出中间值mid在序列中是第几小的数字,从而改变max值和min值,再不断的求下去。但一开始还是超时了,

 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <cstring>
 4 #include <string>
 5 #include <algorithm>
 6 #define MAX 100009
 7  
 8 long long A[MAX];
 9 long long B[MAX];
10  
11 int cmp(const void *a, const void *b) {
12     long long at = *(long long *)a;
13     long long bt = *(long long *)b;
14     return (int)(at - bt);
15 }
16  
17 long long findbit(long long mid, long long n, long long m) {
18     int ans = n*m;
19     for(long long int i = 0; i < n; i++) {
20         for(long long int j = m -1; j >= 0; j--) {
21             if(A[i] + B[j] > mid) {
22                 ans--;
23             }
24             else {
25                 break;
26             }
27         }
28     }
29     return ans;
30 }
31  
32 int main(int argc, char const *argv[])
33 {
34     long long int m, n, k;
35     //freopen("input.txt","r",stdin);
36     while(scanf("%lld %lld %lld",&m, &n, &k) != EOF) {
37         for(int i = 0; i < m; i++) {
38             scanf("%lld",&A[i]);
39         }
40         for(int i = 0; i < n; i++) {
41             scanf("%lld",&B[i]);
42         }
43  
44         qsort(A, n, sizeof(long long),cmp);
45         qsort(B, m, sizeof(long long),cmp);
46  
47         long long max = A[n - 1] + B[m - 1];
48         long long min = A[0] + B[0];
49         long long mid;
50          
51         while(min <= max) {
52             mid = (max + min)/2;
53             long long midBit = findbit(mid, n, m);
54             if(k <= midBit) {
55                 max = mid - 1;
56             }
57             else {
58                 min = mid + 1;
59             }
60         }
61  
62         printf("%lld
",min);
63     }
64     return 0;
65 }

超时的原因在于findBit内的二层循环。我们如果将第一个数组从小到大遍历,而将第二个数组从大到小遍历,比如下面

6 9 11

8 11 13

如果a[0] + b[1] > 15, (6 +11 > 15),那么a[1] + b[1] 必然也大于15,因为a[1] > a[0], a[1] + b[2]也必然大于15,因为b[2] > b[1]

所以需要遍历每一个a,而对于b只需要进行局部的遍历。

也就是说,对于a[0]生成的序列,mid是第mid0 = j+1小的数字,那么对于a[1], j只需要从上一个j开始算起,因为前面的肯定比mid要大。

代码如下

 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <cstring>
 4 #include <string>
 5 #include <algorithm>
 6 #define MAX 100009
 7  
 8 long long A[MAX];
 9 long long B[MAX];
10  
11 int cmp(const void *a, const void *b) {
12     long long at = *(long long *)a;
13     long long bt = *(long long *)b;
14     return (int)(at - bt);
15 }
16  
17 long long findbit(long long mid, long long n, long long m) {
18     long long int ans = 0;
19      
20     long long int j = m - 1;
21     for(long long int i = 0; i < n; i++) {
22         while(j >= 0 && A[i] + B[j] > mid) {
23             j--;
24         }
25         ans = ans + j + 1;
26     }
27     return ans;
28 }
29  
30 int main(int argc, char const *argv[])
31 {
32     long long int m, n, k;
33     //freopen("input.txt","r",stdin);
34     while(scanf("%lld %lld %lld",&m, &n, &k) != EOF) {
35         for(int i = 0; i < m; i++) {
36             scanf("%lld",&A[i]);
37         }
38         for(int i = 0; i < n; i++) {
39             scanf("%lld",&B[i]);
40         }
41  
42         qsort(A, n, sizeof(long long),cmp);
43         qsort(B, m, sizeof(long long),cmp);
44  
45         long long max = A[n - 1] + B[m - 1];
46         long long min = A[0] + B[0];
47         long long mid;
48          
49         while(min <= max) {
50             mid = (max + min)/2;
51             long long midBit = findbit(mid, n, m);
52             if(k <= midBit) {
53                 max = mid - 1;
54             }
55             else {
56                 min = mid + 1;
57             }
58         }
59  
60         printf("%lld
",min);
61     }
62     return 0;
63 }
原文地址:https://www.cnblogs.com/jasonJie/p/5709909.html