厚積薄發—和定值,求乘積最小的組合

Q:輸入一個遞增排序的數組和一個數字S,在數組中查找兩個數,是的他們的和正好是S,如果有多對數字的和等於S,輸出兩個數的乘積最小的。

思路:

1,因爲數組滿足遞增,從首尾開始遍歷 i,j,如果ai+aj=S,就是答案 (相差越大,乘積越小,所以第一個遇到就是乘積最小的)

2,如果 ai+aj>S, aj肯定不是答案之一,j--   (aj後面的不可能,和只會更大)

3,如果 ai+aj<S, ai肯定不是答案之一,i++ (ai前面的不可能,和只會更小)

複雜度O(n)

 

題目關鍵在於證明:和爲定值的兩個數,相差越遠乘積越小。

 

首先是圖形解釋(只適用於 S>0)

 

下面做數學上的證明:

所取的兩個數分別爲 x 和 S-x

那麼乘積可表示爲:f=x*(S-x)

兩個數的差 y=\left\{\begin{matrix} S-2x,x<S/2\\ 2x-S,x\geq S/2 \end{matrix}\right., 用y表示x得到 x=\left\{\begin{matrix} (S-y)/2,x<S/2\\ (S+y)/2,x\geq S/2 \end{matrix}\right.

進一步乘積可以表示爲:f=(S^{_{2}}-y^{_{2}})/4

顯然,乘積 f 是隨  y單調遞減的,即可證明再實數範圍內,和爲定值的兩個數 差越大,他們的乘積則越小。

 

簡單的一個demo如下:

#include "stdafx.h"
#include<vector>
#include<iostream>
using namespace std;

vector<int> findMinmul4Sum(vector<int> &arr, int sum)
{
	vector<int> result;
	int n = arr.size();
	int i = 0, j = n - 1;
	while (i < j)
	{
		if (arr[i] + arr[j] == sum) { result.push_back(arr[i]);result.push_back(arr[j]);break; }
		if (arr[i] + arr[j] < sum) i++;
		if (arr[i] + arr[j] > sum) j--;
	}
	return result;
}

int main()
{
	vector<int> a = {1,2,3,4,5,6,7,8,9};
	vector<int> b= findMinmul4Sum(a, 8);
	cout << b[0] <<"\t"<<b[1] << endl;
	system("pause");
	return 0;
}

循環部分可以進一步優化爲

while(i<j)
{
    if(arr[i]+arr[j]==sum) ...
    while(arr[i]+arr[j]<sum && i<j) i++; //i<j必須
    while(arr[i]+arr[j]>sum && i<j) j--;
}

速度可以進一步加快

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