铺设油井管道问题(分治法实现)

铺设油井管道: 某石油公司有n口油井,为方便输送石油,计划修建输油管道。根据设计要求,水平方向有一条主管道,每口油井修一条垂直方向的支线管道通向主管道。请设计一种算法确定主管道的位置,使得所有油井到主管道之间的支线管道长度的总和最小。

输入格式:

每个输入文件为一个测试用例,每个文件的第一行给出一个正整数n(1≤n≤1000000),表示油井数量,从第二行起的n行数据,表示每口油井的位置,每行包含以空格分隔的两个整数,分别表示每口油井的横坐标x(−10000≤x≤10000)和纵坐标y(−10000≤y≤10000)。

输出格式:

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

输入样例:

在这里给出一组输入。例如:

4

-1 1

2 2

5 -1

-3 7

输出样例:

9

题目分析:

要想得到各油井到主管道之间的支管道最小长度总和,则主管道最优位置y应该是所有管道位置yi的中位数,则最小长度之和为每个管道|yi-y|的长度之和。

算法:

1.直接求中位数:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n;     
    int x;     
    int y[1000];    
    cin>>n;
    for(int i=0;i<n;i++)
     cin>>x>>y[i];
     sort(y,y+n);    //按升序排序
    int min=0;
    int mid=y[n/2];
    for(int i=0;i<n;i++)
     min += (int)fabs(y[i]-mid);
    cout<<min<<endl;
}

输出样例:

2.使用分治法求中位数:

分治法:

1.分治法是对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同。
2。递归地解这些子问题,然后将各子问题的解合并得到原问题的解。

分治法的基本步骤:

代码:

#include <stdio.h>
#include <stdlib.h>

void swap(int &a,int &b) 
{
	int t=a;
	a=b;
	b=t;
}
int quick(int y[],int l,int h)  //快速排序,返回数轴位置 
{
	int x=y[l];
	while(l<h)
	{
	    while(l<h&&y[h]>x) h--;
       	swap(y[l],y[h]);
		while(l<h&&y[l]<x) l++;
		swap(y[l],y[h]);
	}
	y[l]=x;
	return l;
}


int middle(int y[],int l,int h,int k)  
{
	if(l==h) return y[l];  
	int i=quick(y,l,h);
	int j=i-l+1; //表示这个数轴是整个数组中第几小的.
	if(k<=j){
		return middle(y,l,i,k);  //如果比第k小的数要大,则在比它小的数中中在找第k小的
	}
	else return middle(y,i+1,h,k-j);  //如果比第k小的数要小,则只需要在比它大的数中找,第k-j大的
}


int main()
{
	int n;
	scanf("%d",&n);
	int x[10000],y[10000];
	for(int i=0;i<n;i++){
		scanf("%d %d",&x[i],&y[i]);
	}
	int mid;
    mid=middle(y,0,n-1,n/2+1);  //寻找中位数 
    int sum=0;
    for(int i=0;i<n;i++){
    	sum+=abs(mid-y[i]);
	}
	printf("%d",sum);
}

输出结果:

分析:

可以使用分治法将原问题分为若干个子问题。先使用quick()返回出第一个数轴的位置l。代表这个数是整个数组中第l+1小的,而我们需要寻找第k=n/2小的,即中位数。用此数轴位置将整个数组分为两个部分,则左边半边都是<y[l]的,右边都是>y[l]的,在分别在这两半边寻找中位数,以此类推。最后当l=h时,找到数组纵坐标的中位数,返回y[l]。

其中在middle函数中,当l=h时返回 y[l]

j=l+1表示为在和数轴中第j小的。若第j小的比第k小的数要大则return middle(y,l,i,k)

反之则return middle(y,i+1,h,k-j)

进行递归,最后得出中位数。再进行相加,最后即可得出答案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值