两个数组的元素之和最大的前k项

题目:

 
 
两个数组,从每个数组中取一个数相加,求最大的前k个和
     数组A:1,2,3
     数组B:4,5,6
     则最大的前2个和:9,8。


方案1: naive method

把两个数组的和先求出来,然后用Nth-Elemnet,找出第k大的数。Nth-Element就是快排变形的那个partition。时间复杂度O(m*n),空间复杂度O(m*n)。

但是可以借助杨氏矩阵来优化这个方法。。。TODO

code略。


方案2:大顶堆,先对两个数组排序,降序。 O(nlgn)

That can be easily done in O(k*logk). I'll only assume arrays are sorted in descending order, to simplify notation. The idea is simple. We'll find 1st, 2nd, .., k-th maximum values one by one. But to even consider pair (i, j) we need to have both (i-1, j) and (i, j-1) already selected, because they both are greater or equal than (i, j).
It's much like if we push all n*m pairs into the heap and then remove max k times. 

void GetKthLargestSum(int arrA[], int lenA, int arrB[], int lenB, int k){
	if (arrA == NULL || lenA == 0 || lenB == NULL || lenB == 0)
		return;

	// sort array A and B, descending order...
	qsort(arrA, lenA, sizeof(int), compare);
	qsort(arrB, lenB, sizeof(int), compare);

	// key: sum(A, B), value: pair<indexOfA, indexOfB>
	priority_queue<int, pair<int, int> > maxHeap;

	int idxA = 0, idxB = 0;
	maxHeap.push(pair<int, pair<int, int> >(arrA[0]+arrB[0], make_pair(idxA, idxB)));
	for (int idx = 0; idx < k; ++idx){
		pair<int, pair<int, int> > val = maxHeap.top();
		cout << val.first << " ";
		maxHeap.pop();

		maxHeap.push(pair<int, pair<int, int> >(arrA[val.second.first+1]+arrB[val.second.second], make_pair(val.second.first+1, val.second.second)));
		maxHeap.push(pair<int, pair<int, int> >(arrA[val.second.first]+arrB[val.second.second+1], make_pair(val.second.first, val.second.second+1)));
	}
	// TODO: 1. Duplicated pairs can be added to the heap, this can be prevented with hash.
	//		 2. Indexes need to be validated, e.g. that max.i + 1 < arrA.length.
}

方案3:近似O(n)方案

先用一个指针指向数组A的头,一个指向数组B的尾,如果A[0] + B[m] < C[k],A的指针后移,判断A[1] + B[m],如果A[0] + B[m] > C[k],则B的指针前移,判断A[0] + B[m-1]。简单说就是< C[k],A的指针后移,> C[k],B的指针前移。不过问题的关键在于计数,同时统计大于C[k]的小于C[k]的数有多少个。如果A[0] + B[m] < C[k],则A[0] + B[0],B[1]......B[m-1]都小于<C[k],小于的计数+m,如果A[0] + B[m] > C[k],则A[1],A[2]......A[n]+B[m]都大于C[k],大于计数+n,这样,就可以写出一个(m+n)*log(Max-Min)+ n*log(n) + m*log(m)的程序。

借鉴这篇文章"Selection in X + Y and matrices with sorted rows and columns" by A. Mirzaian and E. Arjomandi. 

int qceil(int x)
{
	return (x + 3) / 4;
}

int qhigh(int x, int n)
{
	if (n & 1) // odd
		return qceil(x + 2 * n + 1);
	else // even
		return n + 1 + qceil(x);
}

typedef std::vector<int> Vec;

struct Ranks
{
	int ra_less;
	int rb_more;
	Vec l;

	Ranks(const Vec& a1, const Vec& a2, int a, int b)
	{
		int n = a1.size() - 1;
		ra_less = 0;
		rb_more = n * n;
		l.reserve(12 * n + 1);
		l.push_back(0);

		int j = n;
		for (int i = 1; i <= n; ++i)
		{
			while (j && a1[i] + a2[j] <= a)
				--j;

			ra_less += j;
			int jj = j;

			while (jj && a1[i] + a2[jj] < b)
			{
				l.push_back(a1[i] + a2[jj]);
				--jj;
			}

			rb_more -= jj;
		}
	}
};

Vec half(const Vec& a)
{
	Vec res;
	res.reserve(2 + (a.size() - 1) / 2);
	res.push_back(0);

	for (int i = 1; i < a.size(); i += 2)
		res.push_back(a[i]);

	if (a.size() & 1)
		res.push_back(a[a.size() - 1]);

	return res;
}

struct AB
{
	int a;
	int b;
};

AB biselect(const Vec& a1, const Vec& a2, int k1, int k2)
{
	AB res;
	int n = a1.size() - 1;
	assert(n > 0);

	if (n == 1)
	{
		res.a = res.b = a1[1] + a2[1];
	}
	else if (n == 2)
	{
		Vec l {a1[1] + a2[1], a1[1] + a2[2], a1[2] + a2[1], a1[2] + a2[2]};
		std::nth_element(l.begin(),
			l.end() - k1,
			l.end());
		res.a = l[4 - k1];
		std::nth_element(l.begin(),
			l.end() - k2,
			l.end());
		res.b = l[4 - k2];
	}
	else
	{
		int k1_half = qhigh(k1, n);
		int k2_half = qceil(k2);
		AB ab = biselect(half(a1), half(a2), k1_half, k2_half);
		Ranks ranks(a1, a2, ab.a, ab.b);
		int r1 = k1 + ranks.rb_more - n * n;
		int r2 = k2 + ranks.rb_more - n * n;

		if (ranks.ra_less <= k1 - 1)
			res.a = ab.a;
		else if (r1 <= 0)
			res.a = ab.b;
		else
		{
			std::nth_element(ranks.l.begin() + 1,
				ranks.l.end() - r1,
				ranks.l.end());
			res.a = *(ranks.l.end() - r1);
		}

		if (ranks.ra_less <= k2 - 1)
			res.b = ab.a;
		else if (r2 <= 0)
			res.b = ab.b;
		else
		{
			std::nth_element(ranks.l.begin() + 1,
				ranks.l.end() - r2,
				ranks.l.end());
			res.b = *(ranks.l.end() - r2);
		}
	}

	return res;
}

int select(const Vec& a1, const Vec& a2, int k)
{
	assert(a1.size() == a2.size());
	AB ab = biselect(a1, a2, k, k);
	assert(ab.a == ab.b);
	return ab.a;
}

int check(const Vec& a1, const Vec& a2, int k)
{
	int n = a1.size() - 1;
	Vec sum;
	sum.reserve(n * n);

	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j)
			sum.push_back(a1[i] + a2[j]);

	std::nth_element(sum.begin(),
		sum.end() - k,
		sum.end());
	return *(sum.end() - k);
}

#include <random>

int sovle_two_arrays_sum()
{
	std::random_device rd;
	std::default_random_engine e1(rd());
	std::uniform_int_distribution<int> uniform_dist(1, 6);

	int n = 100000;
	Vec a1, a2;
	a1.reserve(n+1);
	a2.reserve(n+1);
	int x = 1000;

	a1.push_back(0);
	for (int i = 0; i < n; ++i)
	{
		x -= uniform_dist(e1);
		a1.push_back(x);
	}

	x = 1000;

	a2.push_back(0);
	for (int i = 0; i < n; ++i)
	{
		x -= uniform_dist(e1);
		a2.push_back(x);
	}

	std::cout << select(a1, a2, 1 + n * n / 7) << '\n'
		/*<< check(a1, a2, 1 + n * n / 7) << '\n'*/;

	return 0;
}

Ref:

http://stackoverflow.com/questions/18557175/how-to-find-pair-with-kth-largest-sum

http://www.51nod.com/question/index.html#!questionId=46


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值