7-2 输油管道问题

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

给定n口油井的位置, 计算各油井到主管道之间的输油管道最小长度总和。

输入格式:

输入的第1 行是油井数n,1<=n<=10000。接下来n 行是
油井的位置,每行2个用空格割开的整数 x和 y,-10000<=x,y<=10000。

输出格式:

输出油井到主管道之间的输油管道最小长度总和。

输入样例:

5
1 2
2 2
1 3
3 -2
3 3

输出样例:

6
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;

const int mas=1e5+5;
int n,y[mas],res;

int quick_sort(int l,int r,int k);
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
       // getchar(),getchar(),getchar();//分别吸收空格 数据x 空格,这里造成的段错误
        int temp;
         scanf("%d%d",&temp,&y[i]);
    }//这道题本质上就是来求y坐标的中位数,与x的数据无关。因为在中位数时,距离和最短
    int x=quick_sort(1,n,(n+1)/2);//这里也有可能够会造成段错误
    for(int i=1;i<=n;i++)
        res+=abs(x-y[i]);
    cout<<res<<endl;
    return 0;
}
int quick_sort(int l,int r,int k)//名字相同也会造成段错误
{
   // if(l>=r)return y[l];
    int i=l-1,j=r+1,pos=y[l];//注意区分j=++r与j=r+1;
    while(i<j)
    {
        while(y[++i]<pos);
        while(y[--j]>pos);//这里会造成段错误
        if(i<j)swap(y[i],y[j]);
    }//i,就,一般不相同。i代表着左侧小于,j代表着右侧大于//只能够时j
    if(j-l+1==k)return y[j];//解释为什么时n/2
    if(j-l+1<k)return quick_sort(j+1,r,k-j+l-1);
    else return quick_sort(l,j,k);
}

解题思路:

(1)油管是从西向东的,求图上的所有点(代表油田)到油管的距离最小值。

已知油管是自西向东, for(int i=1;i<=n;i++) res+=abs(x-y[i]); 其中y[i]是指油田的纵坐标,x是指油管的纵坐标。因为y[i]属于已知的,所有求min(res),就是确定x的坐标。

(2)根据自己推导。

当n为奇数的时候,取中位数的数值作为x的值取到最小值。

当n为偶数的时候,以最中间的两个数值形成的区间的任意的一个数值都可以作为x的数值都可以取得最小值。

总结:为了方便,不进行情况的分解,中位数便是第(n+1)/2个数。即:奇数取得中位数。偶数取得最中间的两个数中的最小值。

(3)怎样求中位数

1.可以根据采用头文件algorithm,再利用sort函数,取得(n+1)/2的数值。

2.(1)采用快排(本质上就是二分 分治法)过程进行剪枝,降低时间的复杂度,虽然不会在数量级上进行大幅度削减,但是在一定程度上降低。

   (2)怎样实现:

     int quick_sort(int l,int r,int k);数组的起点是l,终点是r,k是指要求的中位数,在现在的这个分支       中从l开始数,是第k个。最开始是第(n+1)/2,起点是1,终点是n;

一次快排的时候两个指针不一定会重合,最终会重合或者i-j==1

i-j==1举例:4 7 6 5 3 2 81 ,最后一步两者进行了交换。

最终会重合举例:1 2 3 4 5 6 7 8,最后是一方走到另一方的 。

如果i与j进行重合,则在他们左侧的都是小于pos,右侧都是大于pos,自己此时的位置可能比pos大,也可能够比pos小,这个得看是最后一步是谁移动的。

如果j<i,则包括位置j在内的所有数都比pos小,包括i在内的所有数值都比pos大。

总结:在任何情况下,i左边的数都是小于pos的,j右边的数都是大于pos的,如果两个数相遇,或者穿过就一定能够是满足分成两部分的。

进行一次快排以后,看看此时的位置的是否等于k,如果是则返回,如果小于 if(j-l+1<k)return quick_sort(j+1,r,k-j+l-1);如果大于return quick_sort(l,j,k);

同时里边涉及到快排的模板,但是由于快排的模板涉及到边界问题,所以最好记住一种模板。而且快排取pos数值可以取到l,r,(l+r)>>1,数据范围如果太大,采用l,r会造成超时。

一般情况下,快排采用的是递归分治法,一定要有结束的标志,但是这一道题,因为是采用了减治法,情况一定存在,所以不需要使用,有结束递归的方法。

遇到的编程问题:

其中最主要的问题便是段错误。

(1)因为快排的模板没有记住,造成段错误,多次造成了边界问题。

(2)因为x的数据没有实质的作用,所以一开始我就是用想着用连着的3getchar(),分别吸收上一个数据的回车,数据x,以及空格,如:getchar(),getchar(),getchar();                                                 但是分别吸收空格 数据x 空格,这里造成的段错误,因为数据x不是一个字符,可能够是多个字符,比如-10000,然而一个getchar(),只能够吸收一个字符。因此我就采用设置一个变量temp,并且吸收每一个x的数据。

(3)int i=l-1,j=r+1,pos=y[l];还有就是注意到,i=l-1不是等于i=--l;l和r的数值不能发生变换

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值