給定兩個整型數組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
這個題是把兩個數組裏面的元素求和後變成新的一個數組C,問數組C中第K大的數字是多少。
題目的數據比較大,最大的K是100000*100000,儘管時限是2秒,但是如果是直接枚舉生成前K個數字的話,還是不能通過測試的。
那麼得想想其他辦法,比如說二分,但是該二分什麼呢,一下子很難想到,一般情況下都是二分給定的數據中有的數字,但是這兒行不通。
後來想到了可以直接二分答案,儘管這個答案不在生成的數列中。
我們可以用二分逼近。
二分一個答案X
然後統計一下生成的序列中<=X的有多少個。
如果個數>=K,那麼這是一個可能的解,記錄一下。
然後我們往小的逼近。
如果<K說明當前的答案不夠大。
要往大的逼近。
計算比X小的數字有多少個可以利用數組的單調性來做。
先對a,b數字從小到大排序。
然後枚舉a中的元素a,統計一下b中有多少個和a加起來是<=X的。
a<=a[i+1]那麼b中的符合的個數將會單調不增。
這樣驗證的複雜度就是O(n+m)了。
最後的總複雜度是O(log(2*10^9)*(n+m))
完美的複雜度。
#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;
typedef long long lld;
const lld M = 100001;
lld arr1[M],arr2[M];
lld m,n,k;
//統計小於等於 mid 的個數
lld cntMin(lld mid){
lld cnt = 0;
lld j = n-1;
for(int i=0; i<m; i++){
//由於已排序,可直接利用上一次結果
while(j >= 0 && arr1[i] + arr2[j] > mid) j--;
cnt += j+1;
}
return cnt;
}
lld low,high,mid;
int main() {
freopen("in.txt", "r", stdin);
while(scanf("%lld%lld%lld", &m,&n,&k) != EOF){
for(lld i=0; i<m; i++) scanf("%lld", &arr1[i]);
for(lld j=0; j<n; j++) scanf("%lld", &arr2[j]);
sort(arr1, arr1+m);
sort(arr2, arr2+n);
low = arr1[0] + arr2[0];
high = arr1[m-1] + arr2[n-1];
lld ans;
//二分逼近
while(low <= high){
mid = (low+high)/2;
lld cnt = cntMin(mid);
if(cnt >= k){
ans = mid; //mid有可能是解
high = mid-1;
}else
low = mid+1;
}
printf("%lld\n",ans);
}
return 0;
}