输油管道问题-分治

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

如果有两口油井,取两口油井南北方向之间的任意位置
如果有三口油井,则取中位数。
n口油井即为找中位数
下面用分治的思想求中位数,而不是用C语言自带的sort()函数

#include<stdio.h>
#include<stdlib.h>//abs()求绝对值函数的头文件
#define N 10010
int b[N],a[N];

void swapAij(int i,int j)
{
    int temp;
    temp=a[i];
    a[i]=a[j];
    a[j]=temp;
}

int SearchMid(int s,int e,int t)//从a[s]到a[e],找第t小的
{
    int nleft=0;
    //printf("从a[%d]到a[%d]查找第%d小的数\n",s,e,t);
    //for(int k=s; k<=e; k++)
        //printf("%d ",a[k]);
    //printf("\n");
    int i,j=e;
    //用i,j分别从左到右,从右到左进行遍历使比a[s]小或等于a[s]的都在左子集中,用nleft计数
    for(i=s+1; i<=j; i++)
    {
        if(a[i]<=a[s])
            nleft++;//左子集中的个数加一

       //a[i]>a[s]时,从右到左找a[j]<=a[s]
       //交换两个数a[i]和a[j],nleft++
        else
        {
            for(; j>i; j--)
               if(a[j]<=a[s])
                {
                    swapAij(i,j);//交换
                    nleft++;//左子集个数++
                }
        }
    }

    //a.未比较a[j]时,i=e且比较a[i]和a[s]后退出循环
    //b.比较过a[j]时,a[j]>a[s],i=j退出循环

    //左子集的个数等于t-1,则找到了第t小的数a[s]
    if(nleft+1==t)
        return a[s];

    //左子集的个数nleft小于t-1,则舍弃左子集和a[s],找右子集中第t-nleft-1个
    else if(nleft+1<t)
      SearchMid(s+nleft+1,e,t-nleft-1);

    //nleft+1>t,在左子集中找第t小的数
    else
        SearchMid(s+1,i-1,t);
}

int main()
{
    int n,mid,sum=0;
    scanf("%d",&n);
    for(int i=0; i<n; i++)
        scanf("%d %d",&b[i],&a[i]);
    mid=SearchMid(0,n-1,n/2+1);//求中位数即 从a[0]到a
    [n-1]找第n/2-1小的
    //printf("mid=%d\n",mid);
    for(int i=0; i<n; i++)
        sum+=abs(a[i]-mid);
    printf("%d",sum);
    return 0;
}

/*
输入样例
5
1 2
2 2
1 3
3 -2
3 3
5
1 3
3 -2
3 3
1 2
2 2
输出样例
6
*/

猜你喜欢

转载自blog.csdn.net/qq_33390700/article/details/51762264