分治学习日记(一)

Divide-Conquer

例1、主油管道为东西向,确定主油管道的南北位置,使南北向油井喷油管道和最小。要求线性时间完成。

输入要求:

输入有油井数量行,第 K 行为第 K 油井的坐标 X ,Y 。其中, 0<=X<231,0<=Y<231 。

输出要求:

输出有一行, N 为主管道最优位置的最小值

注意:用快排做的不给分!!

友情提示:可以采用while(scanf("%d,%d",&x,&y) != EOF)的数据读入方式。

题目分析:

简易分析得出x坐标无用,问题的意思为求y坐标的中位数。

分治的入门题,主要是考察求中位数的算法。

关键在于pivot的选择,有点像快排

(一)RandomizedSelect算法

即随机选取pivot,最坏情况O(n^2), 所以必tle,只是为了理解入门,具体分治过程如下:

分:随机选一个数为pivot,这里以最后一个数为例子,把比pivot小的移到LHS,大的移到RHS。

治:定义递归终点为所找的第k大的数,即k = n/2

​ 如果所找的pivot为结果,返回pivot,

​ 比pivot小则向LHS递归,否则向RHS递归

Before:

-127498-61225
headtailpivot

After(one partition):

-1224-6591278
pivot

代码包括两个部分:Partition和Select

Partition:

//选定pivot,将比pivot小的数移到pivot左边 ,比pivot大的数移到pivot右边,并返回pivot所在位置 
int Partition(int a[],int head, int tail){	
	int i, j ;
	int pivot_value, pivot_index;
	pivot_value = a[tail];
	pivot_index = tail;
	i = head;
	j = tail - 1;
	while(i < j)
	{
		while(a[i] < pivot_value)
		{
			i++;
		}
		while(a[j] > pivot_value)
		{
			j--;
		}
		if(i < j)
			swap(a[i], a[j]);
	}
	swap(a[i], a[pivot_index]);
	return i;	
}

Select:

//找到第k大的数 
int Select(int head, int tail, int k){	
	int pivot_index;
	pivot_index = Partition(head, tail);
	if(pivot_index - head + 1 == k)
	{
		return a[pivot_index];
	}
	else if(pivot_index - head + 1 > k)
	{
		return Select(head, pivot_index-1, k);
	}
	else
	{
		return Select(pivot_index + 1, tail, k - (pivot_index - head + 1));
	}
}

之前已经提及,这个算法容易理解,但由于pivot选取是随机的,会导致算法不稳定,必tle,因此下面是改进的方法;

(二)BFPTR算法

先分组,找各组的中位数,再找出中位数的中位数

First:

-127498-61225
1111122222
-127498-61225
pivot 1pivot 2

Then:

457-198-61222
pivot 1pivot 2

因此只需在RandomizedSelect的基础上+BFPTR找pivot的函数即可

find_pivot:

//预处理,找到比较接近真实中位数的分组中位数的中位数的值 
int find_pivot(int a[], int head, int tail)
{
	int i;
	int num = 0;
	for(i = head; i + 4 <= tail; i+=5)
	{
		sort(&a[i], &a[i+4]);
		swap(a[head + num], a[i+2]);
		num++;
	}
	if(tail > i)
	{
		sort(&a[i], &a[tail]);
		swap(a[head + num], a[(i+tail) / 2]);
		num++;
	}
	if(!num || num == 1)
	{
		return a[head];
	}
	else
	{
		return find_pivot(a, head, head + num - 1);
	}	
} 
//预处理,找到比较接近真实中位数的分组中位数的中位数的位置
int find_pivot_index(int a[], int head, int tail, int pivot)
{
	int i;
	for(i = head; i <= tail; i++)
	{
		if(a[i] == pivot)
		{
			return i;
		}
	}
}

总结:思路还是很好想的,但中间细节也比较多,例如边界的确定,参考了其他大佬的思路,因为只是自己整理复习用,所以懒得标注了

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值