题目:
有两个数组a,b,大小都为n,数组元素的值任意,无序; 要求:通过交换a,b中的元素,使数组a元素的和与数组b元素的和之间的差最小 |
很久之前就看到过这条题目,思路是有的,也没看网上其他人是怎么解的:
1. A,B需要分别建最大堆(复杂度2N),维护各自的和值sumA,sumB,
2. 如果堆A和堆B都非空;
2. 如果sumA>=sumB,且{若max(A)>=max(B),交换A堆的根与B堆的根;若max(A)<max(B),不用交换},把堆B根加入到sumB中,去掉B堆的根,重新调整堆B;
3. 如果sumA<sumB,且{若max(A)<=max(B),交换A堆的根与B堆的根;若max(A)>max(B),不用交换},把堆A根加入到sumA中,去掉A堆的根,重新调整堆A;
4. 返回2
最后得到交换后的数组与各自的和值sumA和sumB;
复杂度计算:建堆复杂度为2N,每次循环要调整一次推,复杂度为lgN,有2N次循环,所以复杂度为NlgN。
把代码写出来,练下手,先写直接点的数据版本:
void MaxHeapify(int * src, int pos,int count)
{
int l = (pos<<1)+1;
int r = l+1;
int maxpos = pos;
if (l<count && src[l]>src[pos])
{
maxpos = l;
}
if (r<count && src[r]>src[maxpos])
{
maxpos = r;
}
if (maxpos != pos)
{
int temp = src[maxpos];
src[maxpos] = src[pos];
src[pos] = temp;
MaxHeapify(src,maxpos,count);
}
}
void CreateMaxHeap(int * src,int count)
{
for (int t = count>>1;t>=0;--t)
{
MaxHeapify(src,t,count);
}
return;
}
void swap(int &left,int &right)
{
int temp = left;
left = right;
right= temp;
}
void SwapToGetMinDiff(int *A,int *B,int count)
{
CreateMaxHeap(A,count);
CreateMaxHeap(B,count);
int *pA = &A[0];
int *pB = &B[0];
int sumA = 0;
int sumB = 0;
while ( pA!=&A[count]
&& pB!=&A[count])
{
if ( sumA>=sumB )
{
if (*pA >= *pB)
{
swap(*pA,*pB);
// 调整pA堆
MaxHeapify(pA,0,&A[10]-pA+1);
}
sumB += *pB;
pB++;
//调整pB堆
MaxHeapify(pB,0,&B[10]-pB+1);
}
else
{
if (*pA <= *pB)
{
swap(*pA,*pB);
//调整pB堆
MaxHeapify(pB,0,&B[count-1]-pB+1);
}
sumA += *pA;
pA++;
//调整pA堆
MaxHeapify(pA,0,&A[count-1]-pA+1);
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
int A[] = {0,1,2,3,4,5,6,7,8,9};
int B[] = {10,11,12,13,14,15,16,17,18,19};
SwapToGetMinDiff(A,B,10);
int count = sizeof(A)/sizeof(A[0]);
int sumA = 0;
int sumB = 0;
for (int i = 0; i<count;++i)
{
printf("A[%d] = %d; \tB[%d] = %d;\n",i,A[i],i,B[i]);
sumA+=A[i];
sumB+=B[i];
}
printf("sum of A[] is %d\n",sumA);
printf("sum of B[] is %d\n",sumB);
return 0;
}
上一张图
上面的代码还有优化的空间,有时间的时候再整理一下。一直以A[0],B[0]为堆根,出堆的数据交换到尾段部分,初想有一点作用,但想了想,并没有降低复杂度。
其实如果直接两个数组排序,复杂度为NlgN,再维护两个指针遍历两个数组,维护两个和值,最终的复杂度也是NlgN,而且觉得更好实现。