分治法思考题

 一.求解a^n

1.问题描述:

采用分治策略来求解a^n。

2.问题分析:

(1)如果采用常规方法,n个a相乘,算法的复杂度是O(n)

(2)如果采用分治策略,算法的复杂度则可大大减少

a.当n为偶数时:a^n =(a^n/2) * a^n/2;
b.当n为奇数时:a^n = a^(n-1)/2 * a^(n-1)/2*a;
c.当n=1时: a^n=a;
此时复杂度减少为O(logn)

3.Code:

本题采用递归分治的方法进行求解。 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

//递归求解(位运算会快一些)
ll fac(int a, int n){
	if (n == 1) return a;
	if (n & 1)//判断n是否为奇数
		return pow(fac(a, (n - 1) / 2), 2) * a;
	else 
		return pow(fac(a, n / 2), 2);
}

int main(){
	int a, n;
	while (~scanf("%d%d", &a, &n)) cout << "递归求解:" << fac(a, n) << endl;
	return 0;
}

4.代码运行截图:

5.拓展:

快速幂原理简述:

  

下面给出快速幂做法

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int mod = 1e9 + 7;

ll quickmi(ll a, ll b){
	ll res = 1;
	while(b){
		if(b & 1) res = res * a % mod;
		a = a * a % mod;
		b >>= 1;//左移,即b / 2。
	}
	return res;
}
int main(){
	int n,m;
	cin >> n >> m;
    cout << quickmi(n,m);
	return 0;
}

 二.求解第K小的数

1.问题描述:

给定一个长度为n的无序整数数列,以及一个整数k,求出数列从小到大排序后的第k个数(即求解第K小的数)。 

2.问题分析:

基于分治中快排的思想,从数组a[]中找出一个基准值v,把数组分为两部分a[l...j]和a[j+1...r]。a[l...j]中的元素小于v,a[j+1...r]中元素大于v。这时有两种情况:

a[l...j]中元素的个数大于等于k,则递归到数组a[l...j]中搜索的第k小的数。
a[l...j]中元素的个数小于k,则递归到数组a[j+1...r]中第k-(j-l+1)小的数
因为每次分区完只需要继续操作一边,所以该算法的平均时间复杂度是O ( n ) 

3.Code:

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N];

int find(int l, int r, int k){
    if(l >= r) return a[l];//两指针相遇就返回
    
    int i = l - 1, j = r + 1;//防溢出
	int v = a[l + r >> 1];
    
    //把数组分为两部分a[l...j]和a[j + 1...r],其中a[l...j]中的元素小于v,a[j + 1...r]中元素大于v
    while(i < j){//由于内部的while遇到一次不满足的即停止,因此需要外面套一个大while保证整个过程的正常进行
        do i ++; while(a[i] < v);
        do j --; while(a[j] > v);
        if(i < j) swap(a[i], a[j]);
    }
    //选择区间后进行递归
    if(j - l + 1 >= k) return find(l, j, k);
    else return find(j + 1, r, k - (j - l + 1));
}

int main(){
    int n, k;
    cin >> n >> k;
    
    for(int i = 0; i < n; i ++) cin >> a[i];
    
    cout << find(0, n - 1, k) << endl;
    
    return 0;
}

ps:本题解巧妙之处:不同于两种朴素快排写法,对于快排的实现中i与j两指针可以交错,不影响功能的实现。 

4.代码运行截图:

 三.如何降低分治法的时间复杂度

1.分治中的平衡原则:一般情况下,子问题规模几乎相等时,分治法效率较高。

2.分治可进行二分,三分等等,具体怎么分,需看问题的性质和分治后的效果。只有认真分析分治后可能产生的预期效率,才能灵活地运用分治思想解决实际问题。        

3.在优化分治方面,通过减少子问题个数,减少子问题规模实现。 减少子问题个数意味着减少分治次数。减少子问题规模意味着提高每个子问题处理效率,均可降低分治法的时间复杂度。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,我知道你要我回答的是关于分治法的面试题。 题目描述:有一个大矩形和若干个小矩形,小矩形可能重叠,求用这些小矩形覆盖大矩形的最小面积。 解题思路:这是一个经典的分治问题,我们可以将大矩形分成四个小矩形,然后分别求出每个小矩形被覆盖的最小面积,最终的答案就是这四个小矩形被覆盖的最小面积的和。 具体实现:我们可以使用递归来实现这个算法,具体步骤如下: 1. 如果小矩形集合为空,则返回大矩形的面积。 2. 将大矩形分成四个小矩形 A、B、C、D。 3. 对于每个小矩形,计算它被覆盖的最小面积。 4. 返回四个小矩形被覆盖的最小面积的和。 代码实现如下: ```python class Rectangle: def __init__(self, x1, y1, x2, y2): self.x1, self.y1, self.x2, self.y2 = x1, y1, x2, y2 def min_area(rectangles): def helper(rectangles): if not rectangles: return float('inf') x1 = min(rectangle.x1 for rectangle in rectangles) y1 = min(rectangle.y1 for rectangle in rectangles) x2 = max(rectangle.x2 for rectangle in rectangles) y2 = max(rectangle.y2 for rectangle in rectangles) if x1 >= x2 or y1 >= y2: return 0 area = (x2 - x1) * (y2 - y1) for i in range(len(rectangles)): if rectangles[i].x1 >= x2 or rectangles[i].x2 <= x1 or rectangles[i].y1 >= y2 or rectangles[i].y2 <= y1: continue area -= (rectangles[i].x2 - rectangles[i].x1) * (rectangles[i].y2 - rectangles[i].y1) return min(helper([rectangle for rectangle in rectangles if rectangle.x2 <= x1 or rectangle.y2 <= y1 or rectangle.x1 >= x2 or rectangle.y1 >= y2]), helper([rectangle for rectangle in rectangles if rectangle.x2 <= x1 or rectangle.y2 <= y1 or rectangle.x1 >= x2 or rectangle.y1 >= y1]), helper([rectangle for rectangle in rectangles if rectangle.x2 <= x1 or rectangle.y2 <= y2 or rectangle.x1 >= x2 or rectangle.y1 >= y1]), helper([rectangle for rectangle in rectangles if rectangle.x2 <= x2 or rectangle.y2 <= y1 or rectangle.x1 >= x1 or rectangle.y1 >= y2])) + area return helper(rectangles) ``` 这个算法时间复杂度是 O(nlogn),其中 n 是小矩形的数量,因为每次递归都会将小矩形的数量减半。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

那就随便一点

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

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

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

打赏作者

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

抵扣说明:

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

余额充值