【油井问题】线性时间选择问题

北理工乐学题目:

  • 主油管道为东西向,确定主油管道的南北位置,使南北向油井喷油管道和最小。要求线性时间完成。
    1<= 油井数量 <=2 000 000
    输入要求:
    输入有油井数量行,第 K 行为第 K 油井的坐标 X ,Y 。其中, 0<=X<231,0<=Y<231
    输出要求:
    输出有一行, N 为主管道最优位置的最小值
    注意:用快排做的不给分!!

    测试输入:
    41,969978
    26500,413356
    11478,550396
    24464,567225
    23281,613747
    491,766290
    4827,77476
    14604,597006
    292,706822
    18716,289610
    5447,914746

    测试输出:597006

在这一节中我们要讨论排序问题类似的元素选择问题,问题的一般提法是:给定线性序集中n个元素和一个整数k,要求这n个元素中第k小的元素,本题的【油井问题】即是找中位数,即找第(n / 2)或第(n / 2 + 1)小的元素。

我们用RandomizedSelect算法:

这里介绍说一下我的code过程:

//先上主函数,这里y数组是事先定义了的
int main()
{
    int n = 0,i;
    long long ans;
    int x;
    while(scanf("%d,%d",&x,&y[n]) != EOF)
    {
        n++;
    }
    if (n % 2 == 1)//奇数大数组选第(n / 2) + 1小的数
	    ans = Select(0, n - 1, (n / 2) + 1);
	else
		ans = Select(0, n - 1, n / 2);
    printf("%lld\n",ans);
    return 0;
}

这套代码的核心就在RandomizedSelect算法,也就是其中的Select函数:

long long Select(int l, int r, int k)
{
    if(r - l < 75)//如果数组长度小于或等于75,则用简单排序找出要找的第几位数
    {
        Insertion_sort(l,r);//也可以用冒泡排序
        return y[l + k - 1];
    }
    int i,j;
    for(i = 0; i <= (r - l - 4) / 5; i++)//将数组分成5个一组,共(r-l-4)/5组
    {
        int s = l + 5 * i;
        int t = s + 4;
        Insertion_sort(s,t);//每组排序找出中位数
        Swap(&y[l + i],&y[s + 2]);//将每组的中位数放到数组前面方便后面再找中位数
    }
    int x = Select(l, l + ( r- l - 4) / 5,(r - l + 1) / 10);//找出所找到的中位数的中位数
    i = partition(l, r, x);//把比x小的数放到x前面,大的数放到x后面,返回处理后x的位置
    j = i - l + 1; //此处求得的j的值是中位数的中位数在数组中的排名(第几小)
    if(k <= j)//如果k比j小,那证明所要找的数在l到i之间,那么递归从l到i之间找
        return Select(l, i, k);
    else//反之在i+1到r之间,同样递归找,但是此时k值就要变成k-j,因为数组左边变化了,要找的数的排名就变了
        return Select(i + 1, r, k - j);
}

算法的中心思想就是用递归的方法不停地缩小范围,只是缩小范围的方法有点麻烦,我们是找5个数的中位数,再找这一串中位数的中位数,然后把中位数放到中间,此时一定有(r - l + 1) / 5 * 2 + (r - l + 1) / 10个数比所找的中位数小,再继续递归即可,下面补充一下partition函数和Insertion_Sort函数(仅供参考)

int partition(int l, int r, int x)
{
    int i,j,pos;
    for(i = l; i <= r; i++)
    {
        if(y[i] == x)
        {
            pos = i;
            break;
        }
    }
    Swap(&y[pos],&y[l]);//如果是把y[pos]换到r上,最后交换的则是y[r]和y[i],返回i值
    i = l;
    j = r + 1;
    for(;;)
    {
        while(y[++i] < x && i < r){;}
        while(y[--j] > x){;}
        if(i < j)
        {
            Swap(&y[i], &y[j]);
        }
        else
            break;
    }
    y[l] = y[j];
    y[j] = x;
    return j;
}
void Insertion_sort(int l, int r)
{
    int j,i;
    for(j = l + 1; j <= r; j++)
    {
        long long tmp = y[j];
        for(i = j; i > l && y[i - 1] > tmp; i--)
        {
            y[i] = y[i - 1];
        }
        y[i] = tmp;
    }
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值