c++分治

本文介绍了C++中的分治算法,详细讲解了分治算法的概念和适用条件,并通过战俘赦免和分型盒两个例题进行深入分析,包括问题的解决思路和代码实现。

分治

1. 概念

分而治之,就是分治
分治算法的本质其实就是将一个问题分解为若干个规模较小的相同(相似)子问题,不断解决
这个思维导图看得够清楚了
可以采用分治解决的问题需要满足的条件(特征):
①原问题规模较大,不易直接解决,若是缩小到一定程度就能容易解决
②原问题可以分解为若干个规模较小,求解方式相同(相似)的子问题,且子问题之间的求解是互不影响的
③合并原问题分解的子问题的解可以得到原问题的解
话不多说,直接上例题~

2. 例题

2.1 战俘赦免

2.1.1 题目

题目描述:
现有2^n x 2^n名战俘站在一个正方形方阵等待发落,王决定赦免一部分战俘,将矩阵分为4个更小的正方形矩阵,其中左上角矩阵的所有战俘都将被赦免。剩下的3个小矩阵中,每一个矩阵继续分为4个更小的矩阵,同样的方式赦免战俘,直到无法再分下去为止。
给出n,请输出每名战俘的命运,其中0代表被赦免,1代表不被赦免。
输入格式
一个整数n(1≤n≤10)
输出格式
2n x 2n的矩阵,数字之间有一个空格。
输入样例:
3
输出样例:
0 0 0 0 0 0 0 1
0 0 0 0 0 0 1 1
0 0 0 0 0 1 0 1
0 0 0 0 1 1 1 1
0 0 0 1 0 0 0 1
0 0 1 1 0 0 1 1
0 1 0 1 0 1 0 1
1 1 1 1 1 1 1 1

2.1.2 分析

我们需要两个表示下标的变量,每次确定现在等待发落的是多少行多少列到多少行多少列的战俘,如果是左上角的那个矩阵,就赋值为0,如果不是,就继续分

2.1.3 代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
int a[10000][10000];
void dfs(int n,int f,int j){
   
   
	if(n==2){
   
   
	    a[f][j]=0;
	    return;
	} 
	for(int i=f; i<f+n/2; i++
<think>我们正在讨论C++中的分治算法示例题目和代码实现。根据引用内容,我们可以参考LeetCode上的几个经典分治问题。以下是三个常见的例题及其C++实现:1.**Leetcode50:Pow(x,n)**-实现幂函数2.**Leetcode169:多数元素**-寻找数组中出现次数超过一半的元素3.**Leetcode53:最大子序和**-求连续子数组的最大和下面分别给出这三个问题的分治解法代码实现。###1.Leetcode50:Pow(x,n)题目要求实现计算$x$的$n$次幂的函数,即$pow(x,n)$。使用分治法可以降低时间复杂度到$O(\logn)$。**分治思路**:-将$n$分解为两半,计算$pow(x,n/2)$,然后根据$n$的奇偶性合并结果。-注意处理$n$为负数的情况。```cppclassSolution{public:doublemyPow(doublex,intn){longlongN=n;//防止n为负数时取相反数溢出if(N<0){x=1/x;N=-N;}returnfastPow(x,N);}doublefastPow(doublex,longlongn){if(n==0){return1.0;}doublehalf=fastPow(x,n/2);if(n%2==0){returnhalf*half;}else{returnhalf*half*x;}}};```###2.Leetcode169:多数元素题目要求找出数组中出现的次数大于$\lfloorn/2\rfloor$的元素。**分治思路**:-将数组分成左右两部分,分别求出左半部分的众数和右半部分的众数。-如果左右两部分的众数相同,则直接返回。-如果不同,则在整个数组中统计这两个数出现的次数,返回出现次数多的那个。```cppclassSolution{public:intmajorityElement(vector<int>&nums){returnmajorityElementRec(nums,0,nums.size()-1);}intmajorityElementRec(vector<int>&nums,intleft,intright){//递归终止条件:当子数组只有一个元素时,该元素就是众数if(left==right){returnnums[left];}//分治:将数组分成左右两部分intmid=left+(right-left)/2;intleftMajority=majorityElementRec(nums,left,mid);intrightMajority=majorityElementRec(nums,mid+1,right);//如果左右两部分的众数相同,则直接返回if(leftMajority==rightMajority){returnleftMajority;}//否则,统计左右众数在整个子数组中的出现次数intleftCount=countInRange(nums,left,right,leftMajority);intrightCount=countInRange(nums,left,right,rightMajority);returnleftCount>rightCount?leftMajority:rightMajority;}intcountInRange(vector<int>&nums,intleft,intright,inttarget){intcount=0;for(inti=left;i<=right;i++){if(nums[i]==target){count++;}}returncount;}};```###3.Leetcode53:最大子序和题目要求计算一个整数数组中的连续子数组的最大和。**分治思路**:-将数组分成左右两部分,最大子序和可能出现在左半部分、右半部分或跨越左右两部分。-分别递归求出左半部分和右半部分的最大子序和。-再计算跨越中点的最大子序和(从中点向左扩展和向右扩展,取最大和)。-最后取三者中的最大值。```cppclassSolution{public:intmaxSubArray(vector<int>&nums){returnmaxSubArrayHelper(nums,0,nums.size()-1);}intmaxSubArrayHelper(vector<int>&nums,intleft,intright){if(left==right){returnnums[left];}intmid=left+(right-left)/2;//分别求左右子数组的最大子序和intleftSum=maxSubArrayHelper(nums,left,mid);intrightSum=maxSubArrayHelper(nums,mid+1,right);//求跨越中点的最大子序和intcrossSum=crossSubArray(nums,left,right,mid);//返回三者中的最大值returnmax(max(leftSum,rightSum),crossSum);}intcrossSubArray(vector<int>&nums,intleft,intright,intmid){//从中点向左扩展,求最大和intleftSum=INT_MIN;intsum=0;for(inti=mid;i>=left;i--){sum+=nums[i];leftSum=max(leftSum,sum);}//从中点向右扩展,求最大和intrightSum=INT_MIN;sum=0;for(inti=mid+1;i<=right;i++){sum+=nums[i];rightSum=max(rightSum,sum);}//合并左右两边的和returnleftSum+rightSum;}};```###总结以上三个例题展示了分治算法的典型应用:将问题分解为规模更小的子问题,递归求解,然后合并结果。分治法通常能降低问题的时间复杂度(如从$O(n)$到$O(\logn)$),但需要注意递归的终止条件以及合并结果的逻辑。§§相关问题§§1.分治算法和动态规划有什么区别?2.除了以上例题,还有哪些经典问题可以用分治算法解决?3.分治算法的时间复杂度分析通常使用什么方法?</think>### C++ 分治算法示例题目及代码实现 分治算法通过**分解→解决→合并**三步解决复杂问题,以下是三个经典例题的C++实现: --- #### 1. **Leetcode 50: Pow(x, n)** [^1] **问题**:实现计算 $x^n$ 的函数。 **分治思路**: - 分解:$x^n = x^{n/2} \times x^{n/2}$(若 $n$ 为偶数) - 合并:根据 $n$ 的奇偶性调整结果 ```cpp class Solution { public: double myPow(double x, int n) { long long N = n; // 防止n为负数时溢出 if (N < 0) { x = 1 / x; N = -N; } return fastPow(x, N); } double fastPow(double x, long long n) { if (n == 0) return 1.0; double half = fastPow(x, n / 2); return (n % 2 == 0) ? half * half : half * half * x; } }; ``` **时间复杂度**:$O(\log n)$ --- #### 2. **Leetcode 169: 多数元素** [^1] **问题**:找出数组中出现次数超过 $\lfloor n/2 \rfloor$ 的元素。 **分治思路**: - 分解:将数组分为左右两半 - 解决:递归求左右子数组的众数 - 合并:统计两个众数在整个数组的频率,取更高者 ```cpp class Solution { public: int majorityElement(vector<int>& nums) { return divide(nums, 0, nums.size()-1); } int divide(vector<int>& nums, int l, int r) { if (l == r) return nums[l]; int mid = l + (r - l) / 2; int leftMajor = divide(nums, l, mid); int rightMajor = divide(nums, mid+1, r); // 合并:统计左右众数的全局频率 if (leftMajor == rightMajor) return leftMajor; int leftCount = count(nums, l, r, leftMajor); int rightCount = count(nums, l, r, rightMajor); return leftCount > rightCount ? leftMajor : rightMajor; } int count(vector<int>& nums, int l, int r, int target) { int cnt = 0; for (int i = l; i <= r; ++i) { if (nums[i] == target) cnt++; } return cnt; } }; ``` **时间复杂度**:$O(n \log n)$ --- #### 3. **Leetcode 53: 最大子序和** [^1] **问题**:计算连续子数组的最大和。 **分治思路**: - 分解:将数组分为左右两半 - 解决:递归求左半、右半的最大和 - 合并:计算跨越中点的最大和(关键步骤) ```cpp class Solution { public: int maxSubArray(vector<int>& nums) { return maxSub(nums, 0, nums.size()-1); } int maxSub(vector<int>& nums, int l, int r) { if (l == r) return nums[l]; int mid = l + (r - l) / 2; int leftSum = maxSub(nums, l, mid); // 左半最大和 int rightSum = maxSub(nums, mid+1, r); // 右半最大和 int crossSum = maxCrossing(nums, l, r); // 跨越中点的最大和 return max({leftSum, rightSum, crossSum}); } int maxCrossing(vector<int>& nums, int l, int r) { int mid = l + (r - l) / 2; // 向左扩展的最大和 int leftSum = INT_MIN, sum = 0; for (int i = mid; i >= l; --i) { sum += nums[i]; leftSum = max(leftSum, sum); } // 向右扩展的最大和 int rightSum = INT_MIN; sum = 0; for (int i = mid+1; i <= r; ++i) { sum += nums[i]; rightSum = max(rightSum, sum); } return leftSum + rightSum; } }; ``` **时间复杂度**:$O(n \log n)$ --- ### 分治算法核心要点总结 1. **适用场景**:问题可分解为**相互独立**的子问题(如数组分块、树形结构) 2. **关键步骤**: - **分解**:将原问题划分为规模相同的子问题 - **解决**:递归求解子问题(递归终止条件需明确定义) - **合并**:将子问题的解组合成原问题的解 3. **效率优势**:通过降低问题规模(如二分)将 $O(n)$ 优化至 $O(\log n)$ > 引用说明:示例题目均来自LeetCode经典分治问题[^1],代码实现采用标准分治模板[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值