输油管道问题(线性时间解决)

输油管道问题

某石油公司计划建造一条由东向西的主输油管道。该管道要穿过一个有 n口油井的油田。从每口油井都要有一条输油管道沿南北向最短路经与主管道相连。如果给定 n 口油井的地理位置,即它们的 x 坐标(东西向)和 y坐标(南北向),应如何确定主管道的最优位置,使得各油井到主管道之间的输油管道的长度总和最小?设计一个线性时间算法计算各油井到主管道之间的输油管道最小长度总和。

思考:
主输油管道应该是直的?
南北方向向移动直线使南北方向垂线距离之和最小?
那x坐标应该没什么用了吧?
首次思考看了一波其他人的博客思路,我大概题意理解正确,解决这个问题不难,关键是如何用线性时间来解决,联系所学章节主题—分治法进行解决。
我们可以证明:

如果油井y坐标个数为奇数,主管道y轴坐标肯定是等于某口油井的y坐标,且这口油井y坐标肯定为中位数。
如果油井y坐标个数为偶数,主管道y轴坐标肯定是是在某两口油井y坐标之间,且这两口油井的y坐标值位于所有y坐标的中间。
综上,主管道y坐标肯定为油井坐标值的中位数。

证明方法如下:
(反证法):
1.假设有三口油井,他们的y坐标各不相同,y1<y2<y3,那么主管道一定通过中位数y2。反证:如果主管道在y1与y2之间,y1和y3到主管道的距离不变,但y2到主管道的距离增加。主管道在y2与y3之间同理。当主管道y轴坐标小于y1或大于y3时,距离明显增大。
2.假设有四口油井:y1<y2<y3<y4,那么主管道y轴坐标位于y2和y3之间最好。
3.假设有五口油井:y1<y2<y3<y4<y5,那么主管道坐标等于y3.

要求中位数肯定是要先排序的啊!线性时间排序!
在这里插入图片描述

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <math.h>

using namespace std;
int Partition(int i,int j,int pivort,int A[])
{
    int l=i,r=j;
    while(1){
        while(A[r]>=pivort&&r>l)
            r--;
        if(l<r)
        {
            A[l]=A[r];
        }
        while(A[l]<pivort&&r>l)
            l++;
        if(l<r)
        {
            A[r]=A[l];
        }
        if(r<=l)
            break;
    }
    A[l]=pivort;
    return l;
}
int select(int i,int j,int A[],int l,int n)
{

    if((j-i+1)/5==0)
    {
        sort(A+i,A+j+1);
        return A[l];
    }
    for(int m=i;m<=(j-i+1)/5;m++)
    {
        sort(A+i-1+((m-1)*5+1),A+i-1+((m-1)*5+6));
        float a=(float(l)/float(j-i+1))*5.0;
        swap(A[m+i-1],A[(m-1)*5+i-1+int(a)+1]);
    }
    int x=select(i,(j-i+1)/5+i-1,A,(j-i+1)/10+i-1+1,n);
    if(x!=A[i])
    {
        for(int s=1;s<=n;s++)
        {
            if(x==A[s])
                swap(A[i],A[s]);
        }
    }
    int k=Partition(i,j,x,A);
    if(k==l)
        return x;
    else if (k>l)
        return select(i,k-1,A,l,n);
    else
        return select(k+1,j,A,l,n);
}
int main()
{
    int n;
    double dis=0.0;
    scanf("%d",&n);
    int X[n+1],Y[n+1];
    for(int i=1;i<=n;i++)
    {
        scanf("%d %d",&X[i],&Y[i]);
    }
    int mid=select(1,n,Y,n/2+1,n);
    cout<<"中位数"<<mid<<endl;
    for(int i;i<=n;i++)
    {
        dis =dis+sqrt((Y[i]-mid)*(Y[i]-mid));
    }
    cout<<dis<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值