Google 面試題 第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

這個題是把兩個數組裏面的元素求和後變成新的一個數組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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章