010-最近点对问题-分治法-《算法设计技巧与分析》M.H.A学习笔记

S是平面上n个点的集合,在S中找到两点pq,使得他们的欧几里得距离d(p,q)是所有点对中最小的。

 

朴素的算法是计算所有点对的距离,在求出最小的,需要Ω(n2)

 

采用分治法可以在Θ(nlogn)完成任务。

 

基本思路:

我们用分治范式来解释这一过程:

(a)划分阶段:

用一条垂直线LS划分成两个尽可能大小接近的子集SlSrSl包含左边的n/2各点,Sr包含右边的(n+1)/2个点。分别计算出SlSr中的最近点对距离δl、δr。再计算Sl的点与Sr的点之间的最小距离δ’。

 

(b)治理阶段:

这一阶段需要计算出δ’,如果朴素的计算所有Sl中的点到Sr中所有点的距离,需要Ω(n2),并不会提高效率。幸好我们并不需要比较所有的点。

设δ=min{δl,δr},可以理解,我们要找的点不会再L两边δ距离以外的范围,只会在下图中的灰色部分,我们把这一部分称为T

 

我们有下面这么一个结论:

T中的每个点最多需要和T中的7个点进行比较。(*)

下面解释一下这个结论的由来:

 

我们划分出δ*2δ的矩形,如果该矩形内任意两点之间的距离不超过δ,这个矩形最多能容纳8个点,其中至多4个点属于Sl,至多4个点属于Sr。但有两个点分别在矩形上下边的中点时,可以取得最大点数。因此最多只需要比较7次。

 

现在考虑算法中怎么得知我们需要比较哪7个点呢?

实际操作就是不要考虑,我们尽可以多比较几次,常数的增长并不会带来太大的影响。所以我们可以将T中的点按y轴坐标的增序排列,遍历所有点,但每次只计算某一点和它上面7个点的距离。这里我们进行了一些重复的操作,但保证了最后答案的可靠性,而且带来的只是常数的增长,所以还是可以接受的。

 

(c)组合阶段:

很明显,最近点对距离:δ=min{δl,δr,δ’}。

 

算法分析:

治理阶段需要排序,但我们可以先对点按xy轴坐标进行一次排序并存储,在治理阶段只做提取的操作Θ(n),排序结果依然存在,所以我们的递推式是:

 

经过计算:

算法的复杂度为Θ(nlogn)

 

伪代码:

 

 

 

*实际上只需要4个点,详见周玉林、熊鹏荣、朱洪,《求平面点集最近点对的一个改进算法》。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
最大子段和问题是指在一个数列中到一个子序列,使得该子序列中所有元素的和最大。以下是三种常见的算法实现: 1. 蛮力法 蛮力法是最朴素的解法,它的时间复杂度为 $O(n^2)$。具体实现如下: ```c++ int maxSubArray(int nums[], int n) { int ans = INT_MIN; for (int i = 0; i < n; i++) { int sum = 0; for (int j = i; j < n; j++) { sum += nums[j]; ans = max(ans, sum); } } return ans; } ``` 2. 分治法 分治法的时间复杂度为 $O(n\log n)$,它将问题分成三个部分:求解左半部分的最大子段和、求解右半部分的最大子段和、求解跨越中的最大子段和。具体实现如下: ```c++ int maxSubArray(int nums[], int left, int right) { if (left == right) return nums[left]; int mid = left + (right - left) / 2; int leftMax = maxSubArray(nums, left, mid); int rightMax = maxSubArray(nums, mid + 1, right); int crossMax = nums[mid]; int sum = nums[mid]; for (int i = mid - 1; i >= left; i--) { sum += nums[i]; crossMax = max(crossMax, sum); } sum = crossMax; for (int i = mid + 1; i <= right; i++) { sum += nums[i]; crossMax = max(crossMax, sum); } return max(leftMax, max(rightMax, crossMax)); } ``` 3. 动态规划法 动态规划法的时间复杂度为 $O(n)$,它定义了一个状态数组 $dp$,其中 $dp[i]$ 表示以 $i$ 结尾的最大子段和。具体实现如下: ```c++ int maxSubArray(int nums[], int n) { int dp[n]; dp[0] = nums[0]; int ans = nums[0]; for (int i = 1; i < n; i++) { dp[i] = max(dp[i - 1] + nums[i], nums[i]); ans = max(ans, dp[i]); } return ans; } ``` 以上是三种常见的算法实现,需要注意的是,在实际应用中,我们还可以使用其他优化方法,如前缀和、后缀和、单调栈等,以进一步提高算法效率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

这波lucio来全学了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值