问题描述:
根据中位数定理,要使总和距离最小,应该选择中位数作为目标点。因此解决此问题的方法就是将各个油井投影到y轴,找到y坐标的中位数即可。
解题思路:要找出一个数的中位数,最简单的方法就是对数组进行排序,但是快速排序的时间复杂度也需要O(nlogn)。对与有序数组A[n],如果数组是奇数个,中位数就是A[n/2]。如果数组是偶数个,中位数是中间两个数之和,但是可以发现,当管道选到中间两个数任何一个位置时,距离总和和中位数是一样的,因此,这里选取A[n/2]。
因此该问题变为求解有序数组A[n/2]位置的元素,即第 k=n/2 + 1 小元素。
求解第k小元素
1、先排序,在求解。
2、分治法。
假设元素彼此不等时设计思想:
1、用元素m作为标准将S划分为 S1 与 S2 。其中S1的元素小于m,S2的元素大于m。
2、如果 k <= sizeof(S1),则在S1中找他的第k小元素。
如果 k = sizeof(S1)+1,则m就是第k小元素。
如果 k > sizeof(S1)+1,则就在S2中找第 k-sizeof(S1)-1小元素。
代码实现:
#include<iostream>
#include<stdlib.h>
#include<time.h>
using namespace std;
//第k小元素
int selectK(int arr[], int size, int k)
{
int *small = new int[size];
int *big = new int[size];
int m = arr[0];
int numS = 0, numB = 0;
for(int i = 0; i < size; i++)
{
if(arr[i] < m)
{
small[numS++] = arr[i];
}
if(arr[i] > m)
{
big[numB++] = arr[i];
}
}
if(k <= numS)
return selectK(small, numS, k);
if(k > numS + 1)
return selectK(big, numB, k - numS - 1);
return m;
}
int main()
{
srand((int)time(0));
while(1)
{
int n;
cout << "输入数组规模:";
cin >> n;
int *arr = new int[n];
//随机产生一个数组
for(int i = 0; i < n; i++)
{
arr[i] = rand() % 100;
}
//原数组序列
for(int j = 0; j < n; j++)
{
cout << arr[j] << " ";
}
cout << "\n";
//输油管道问题: 寻找到第 n/2 + 1 小元素即可
int loc = selectK(arr, n, n/2 + 1);
cout << "管道位置:" << loc << "\n" << endl;
delete arr;
}
return 0;
}
假设有相同元素时:
设计思想:1、用m作为标准将S划分为,small,equal,big,三个数组。其中small中的元素小于m,equal中的元素等于m,big 中的元素大于m
2、如果k <= sizeof(small) , 则在small中找第k小元素
如果k > sizeof(small) + sizeof(equal),则在big中找第 k-sizeof(small)-sizeof(equal)
否则 ( k > sizeof(small) && k <= sizeof(small) + sizeof(equal)),则在equal中找第 k-sizeof(small)小元素。
#include<iostream>
#include<stdlib.h>
#include<time.h>
using namespace std;
int selectK(int a[], int length, int k)
{
int *small = new int[length];
int *equal = new int[length];
int *big = new int[length];
int value = a[0];
int numS = 0, numE = 0, numB = 0;
for (int i = 0; i < length; i++)
{
if (a[i] < value)
{
small[numS] = a[i];
numS++;
}
else if (a[i] == value)
{
equal[numE] = a[i];
numE++;
}
else
{
big[numB] = a[i];
numB++;
}
}
if (k <= numS)return selectK(small, numS, k);
else if (k > numE + numS)return selectK(big, numB, k-numS-numE);
else return value;
}
int main()
{
srand((int)time(0));
int n;
cin >> n;
int *array = new int[n];
for (int i = 0; i < n; i++)
{
array[i] = rand()%10000;
}
for (int j = 0; j < n; j++)
{
cout << array[j] << " ";
}
cout << endl;
cout << "管道位置是:" << selectK(array, n, n/2 + 1);
delete array;
return 0;
}