今天学习了线性时间选择,主要是通过快排的方法,在一个平均时间线性的情况下进行第k小元素的选择,结合一道题目进行描述;
题目来自算法设计与分析就是王晓东的那本;
算法实现题2-1 输油管道问题 «问题描述: 某石油公司计划建造一条由东向西的主输油管道。该管道要穿过一个有n 口油井的油 田。从每口油井都要有一条输油管道沿最短路经(或南或北)与主管道相连。如果给定n口油 井的位置,即它们的x 坐标(东西向)和y 坐标(南北向),应如何确定主管道的最优位置, 即使各油井到主管道之间的输油管道长度总和最小的位置?证明可在线性时间内确定主管道 的最优位置。 «编程任务: 给定n 口油井的位置,编程计算各油井到主管道之间的输油管道最小长度总和。 «数据输入: 由文件input.txt 提供输入数据。文件的第1 行是油井数n,1£n£10000。接下来n 行是 油井的位置,每行2个整数x和y,-10000£x,y£10000。 «结果输出: 程序运行结束时,将计算结果输出到文件output.txt 中。文件的第1 行中的数是油井到 主管道之间的输油管道最小长度总和。 输入文件示例 输出文件示例 input.txt 5 1 2 2 2 1 3 3 -2 3 3
output.txt
6
这题目分析一下可以得出就是寻找中位数;为什么呢?将这些数字排列在数轴上寻找一个到各个点距离最小的那个点,可以得知最中间的那个两个点之间的任意位置都是可以的,这是因为其他点到这一点都没有重复的距离出了这个区间都是会存在重复的。所以问题就是寻找这个中位数即可;
那么怎么寻找这个中位数呢?在快排中我们很容易得到一个数在一堆无序数列中的位置,并同时是其左侧是都是比他小的数,右侧都是比他大的数,若这个位置是中间位置那么这个数就是中位数,若这个位置在中间的右侧,那说明中位数在这数的左侧,否则在其右侧,这样不断递归下去一定会找到这个数,最坏的情况下这个算法的效率是n2 。想要加速这个算法就又要使快排过程尽量随机化就是使左右两侧的数的个数尽量相等。这和快排的随机性是一致的。
代码如下:
#include <iostream> #include <cmath> using namespace std; template <class T> int Partion(T a[],int l,int r) { int i=l; int temp; if(l==r) return l; while(l<r) { while(a[r]>=a[i]&&(l<r)) { r--; } if(l<r) { temp=a[r]; a[r]=a[i]; a[i]=temp; i=r; } while(a[l]<=a[i]&&(l<r)) { l++; } if(l<r) { temp=a[l]; a[l]=a[i]; a[i]=temp; i=l; } } return i; } template <class T> int MidSelect(T a[],int l,int r,int mid) { if(l==r) return l; int i=Partion(a,l,r); int j=i-l+1; if(mid<j) { return MidSelect(a,l,i,mid); } else return MidSelect(a,i+1,r,mid-j); } int main() { int num[10010]; int n; int i,j; while(cin>>n) { int a; for(i=0;i<n;i++) { cin>>a>>num[i]; // cout<<num[i]<<endl; } int l,r,mid; l=0; r=n-1; mid=n/2-1; //cout<<mid<<endl; mid=num[MidSelect(num,l,r,mid)]; //cout<<"mid "<<mid<<endl; int sum=0; int add=0; for(i=0;i<n;i++) { add=mid-num[i]; //cout<<"num "<<num[i]; //cout<<add<<endl; if(add>0) sum+=add; else sum-=add; //cout<<sum<<endl; } cout<<sum<<endl; } return 0; }