输油管道问题

问题描述:

根据中位数定理,要使总和距离最小,应该选择中位数作为目标点。因此解决此问题的方法就是将各个油井投影到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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值