分治法之选择问题 (1)

合集下载

NOIP基础算法讲解2

NOIP基础算法讲解2
if(a[i]>a[j])then begin temp[p]:=a[j];inc(p);inc(j);end else begin temp[p]:=a[i];inc(p);inc(i);end
while(i<=mid)do begin temp[p]:=a[i];inc(p);inc(i);end while(j<=right)do begin temp[p]:=a[j];inc(p);inc(i);end for i:=left to right do a[i]:=temp[i]; end;
数据范围:n≤10^6。所有数之和不超过10^9。
例题8:快速幂
【问题】计算an mod k的值 ,其中n<=109。
方法1:朴素算法。每次乘以a,时间复杂度O(n)。 function power(a,n:longint):longint; var x:longint; begin x:=1; for i:=1 to n do x:=x*a; power:=x; end;
时间效率不尽如人意….. 问题出现在哪里呢??
方法2:分治策略
采用分治求解: ➢划分问题:把序列分成元素个数尽量相等的两半; ➢递归求解:统计i和j均在左边或者均在右边的逆序对个数; ➢合并问题:统计i在左边,但j在右边的逆序对个数。
记数列a[st]~a[ed]的逆序对数目为d(st,ed); mid=(st+ed)/2,则有:
三、分治的三步骤
①划分问题:将要解决的问题分解成若干个规模较 小的同类子问题;
②递归求解:当子问题划分得足够小时,求解出子 问题的解。
③合并问题:将子问题的解逐层合并成原问题的解。
四、分治的框架结构
procedure Divide() begin

分治法解决问题的步骤

分治法解决问题的步骤

分治法解决问题的步骤一、基础概念类题目(1 - 5题)题目1:简述分治法解决问题的基本步骤。

解析:分治法解决问题主要有三个步骤:1. 分解(Divide):将原问题分解为若干个规模较小、相互独立且与原问题形式相同的子问题。

例如,对于排序问题,可将一个大的数组分成两个较小的子数组。

2. 求解(Conquer):递归地求解这些子问题。

如果子问题规模足够小,则直接求解(通常是一些简单的基础情况)。

对于小到只有一个元素的子数组,它本身就是有序的。

3. 合并(Combine):将各个子问题的解合并为原问题的解。

在排序中,将两个已排序的子数组合并成一个大的有序数组。

题目2:在分治法中,分解原问题时需要遵循哪些原则?解析:1. 子问题规模更小:分解后的子问题规模要比原问题小,这样才能逐步简化问题。

例如在归并排序中,不断将数组对半分,子数组的长度不断减小。

2. 子问题相互独立:子问题之间应该尽量没有相互依赖关系。

以矩阵乘法的分治算法为例,划分后的子矩阵乘法之间相互独立进行计算。

3. 子问题与原问题形式相同:方便递归求解。

如二分查找中,每次查找的子区间仍然是一个有序区间,和原始的有序区间查找问题形式相同。

题目3:分治法中的“求解”步骤,如果子问题规模小到什么程度可以直接求解?解析:当子问题规模小到可以用简单的、直接的方法(如常量时间或线性时间复杂度的方法)解决时,就可以直接求解。

例如,在求数组中的最大最小值问题中,当子数组只有一个元素时,这个元素既是最大值也是最小值,可以直接得出结果。

题目4:分治法的“合并”步骤有什么重要性?解析:1. 构建完整解:它将各个子问题的解组合起来形成原问题的解。

例如在归并排序中,单独的两个子数组排序好后,只有通过合并操作才能得到整个数组的有序排列。

2. 保证算法正确性:如果合并步骤不正确,即使子问题求解正确,也无法得到原问题的正确答案。

例如在分治算法计算斐波那契数列时,合并不同子问题的结果来得到正确的斐波那契数是很关键的。

分治法

分治法
分 治 法
顾铁成
1
引例:称硬币
如果给你一个装有16枚硬币的袋子,其中有一
枚是假的,并且其重与真硬币不同。你能不能 用最少的比较次数,找出这个假币?

为了帮助你完成这个任务,将提供一台可用来 比较两组硬币重量的仪器,利用这台仪器,可
以知道两组硬币的重量是否相同。
2
引例:称硬币
常规的解决方法是先将这些硬币分成两
15
当 k = 1 时,各种可能的残缺棋盘
16
三格板的四个不同方向
17
【输入】
第一行输入棋盘 的总行数,第二 行输入残缺棋盘 的格子坐标。
【样例输入】 4
4 1
【样例输出】 2 2 3 3 2 1 1 3 4 4 1 5
【输出】
覆盖的矩阵图。
0 4 5 5
18
问题分析
很明显,当K=0时,是不需要三格板的,而当
24
【样例输入】 5 3 23 8 91 56 4 【样例输出】 1
25
问题分析
对于一组混乱无序的数来说,要找到第k
小的元素,通常要经过两个步骤才能实 现:
第一步:将所有的数进行排序; 第二步:确定第k个位置上的数。
26
问题分析
传统的排序算法(插入排序、选择排序
、冒泡排序等)大家都已经很熟悉了, 但已学过的排序方法无论从速度上பைடு நூலகம்还 是从稳定性方面,都不是最佳的。


将7作为一个参照数;
将这个数组中比7大的数放在7的左边; 比7大的数放在7的右边;

这样,我们就可以得到第一次数组的调整:
[ 4 2 6 6 1 ] 7 [ 10 22 9 8 ]
29

用分治法解决问题

用分治法解决问题

分治法所能解决的问题具有以下几个特征: 分治法所能解决的问题具有以下几个特征:
1.该问题的规模缩小到一定的程度就可以容易地解决; 1.该问题的规模缩小到一定的程度就可以容易地解决; 该问题的规模缩小到一定的程度就可以容易地解决 2.该问题可以分解为若干个规模较小且基本相同的子问 2.该问题可以分解为若干个规模较小且基本相同的子问 题。 3.利用该问题分解出的子问题的解可以合并为该问题的 3.利用该问题分解出的子问题的解可以合并为该问题的 解;
分治策略解决问题
问题1 问题1:找出伪币
一个装有1 6枚硬币的袋子 一个装有1 6枚硬币的袋子,1 6枚硬币中有一个 枚硬币的袋子, 6枚硬币中有一个 是伪造的, 是伪造的,并且那个伪造的硬币比真的硬币要轻 一些。你的任务是找出这枚伪造的硬币。 一些。你的任务是找出这枚伪造的硬币。 为了帮助你完成这一任务, 为了帮助你完成这一任务,将提供一台可用来比 较两组硬币重量的仪器,比如天平。 较两组硬币重量的仪器,比如天平。利用这台仪 可以知道两组硬币的重量是否相同。 器,可以知道两组硬币的重量是否相同。
找金块的示例图
方法2 方法2:
n≤2,识别出最重和最轻的金块,一次比较就足够 ≤2,识别出最重和最轻的金块, 了。 n>2,第一步,把这袋金块平分成两个小袋A和B。 第一步,把这袋金块平分成两个小袋A 第二步,分别找出在A 中最重和最轻的金块。 第二步,分别找出在A和B中最重和最轻的金块。设 A中最重和最轻的金块分别为HA 与LA,以此类推, 中最重和最轻的金块分别为HA LA,以此类推, B中最重和最轻的金块分别为HB 和LB。第三步, 中最重和最轻的金块分别为HB LB。第三步, 通过比较HA HB,可以找到所有金块中最重的; 通过比较HA 和HB,可以找到所有金块中最重的; 通过比较LA LB,可以找到所有金块中最轻的。 通过比较LA 和LB,可以找到所有金块中最轻的。 在第二步中, 则递归地应用分而治之方法。 在第二步中,若n>2,则递归地应用分而治之方法。

算法设计与分析课件--分治法-线性时间选择

算法设计与分析课件--分治法-线性时间选择
9
2.5 线性时间选择
这样找到的m*划分是否能达到O(n)的时间复杂度? |A| = |D| = 2r, |B| = |C| = 3r +2,n = 10r +5. |A| + |D| + |C| = 7r + 2 = 7(n-5)/10 +2 = 7n/10 -1.5 < 7n/10 表明子问题的规模不超过原问题的7/10(d)。
T(n) = T(cn) + T(dn) + tn
6
2.5 线性时间选择
Select(S, k) Input: n个数的数组S,正整数k
T(n) = T(cn) + T(dn) + tn
Output: S中的第k个小元素
1. 将S划分成5个元素一组,共[n/5]个组;
2. 每组寻找一个中位数,把这些中位数放到集合M中;
寻找一个分割点m*, 使得左边子表S1中的元素都小于m*, 右子表 S2中的元素都大于m*。 如果寻找m*的时间复杂度达到O(nlogn), 那就不如直接使用排序 算法了。 如果直接寻找m*, 时间复杂度是O(n). 假设选择算法的时间复杂度为T(n), 递归调用这个算法在S的一 个真子集M上寻找m*,应该使用T(cn)时间,这里c是小于1的常数, 反映了M的规模与S相比缩小许多。
✓ 不妨假设n是5的倍数,且n/5是奇数,即n/5 = 2r+1. 于是: |A| = |D| = 2r, |B| = |C| = 3r +2,n = 10r +5.
✓ 如果A和D中的元素都小于m*,那么把它们的元素都加入到S1, S1对应规约后子问题的上限。 类似的,若A和D中的元素都 大于m*, 则把他们的元素都加 入到S2,S2对应规约后子问题 的上限。

算法设计与分析-分治法

算法设计与分析-分治法

3.2.1 归并排序
算法3.1——归并排序
void MergeSort(int r[ ], int r1[ ], int s, int t) {
if (s= =t) r1[s]=r[s]; //只有一个元素,直接赋值 else {
m=(s+t)/2; Mergesort(r, r1, s, m); //归并排序前半个子序列 Mergesort(r, r1, m+1, t); //归并排序后半个子序列 Merge(r1, r, s, m, t); //合并两个已排序的子序列 } }
A、B、C、D 四个区域
Ø想法
Ø 用二维数组data[N][N]表示N×N的方阵,观察方阵中数
字的规律,可以从外层向里层填数。 Ø 设变量size表示方阵的大小,则初始时size = N,填完一
层则size = size - 2;
Ø想法
Ø 设变量begin表示每一层的起始位置,变量i和j分别表示
MergeSort(r,r1,1,1) r1[1]=r[1]
Merge(r1,r,0,0,1)
MergeSort(r,r1,2,3)
MergeSort(r,r1,2,2) r1[2]=r[2]
MergeSort(r,r1,3,3) r1[3]=r[3]
Merge(r1,r,2,2,3)
Merge(r1,r,0,1,3)
• 分治思想 • 归并排序 • 快速排序 • 折半查找 • 选择问题 • 最大子段和问题 • 棋盘覆盖问题 • 循环赛日程安排问题
3.1 基本思想 3.2 排序问题中的分治算法 3.3 查找问题中的分治算法 3.4 组合问题中的分治算法 3.5 典型问题的C++程序(略)

实验1 分治法找到数组元素中的最大值与最小值

实验1 分治法找到数组元素中的最大值与最小值

算法分析与设计实验报告第 1 次实验附录:完整代码#include <time.h>#include <iostream>#include <iomanip>#include <stdlib.h>using namespace std;void min_max(int a[],int i,int j,int &min,int &max) {int mid,max1,max2,min1,min2;if(i==j){max=a[i];min=a[i];return;}if(j==i+1){if(a[i]>a[j]){min=a[j];max=a[i];}else{min=a[i];max=a[j];}}else{mid=(i+j)/2;min_max(a,i,mid,min1,max1);min_max(a,mid+1,j,min2,max2);if(min1>min2)min=min2;elsemin=min1;if(max1>max2)max=max1;elsemax=max2;}}int main (){int m,a[100],min,max;while(1){int f;cout<<"随机数组的规模:";cin>>m;cout<<"随机数的范围:";cin>>f;//计时开始clock_t start,end,over;start=clock();end=clock();over=end-start;start=clock();srand((unsigned)time(NULL));for(int i=1;i<=m;i++){a[i]=(rand()%(f)+0);cout<<a[i]<<' ';}cout<<endl;min_max(a,1,m,min,max);cout<<"最小值:"<<min<<endl;cout<<"最大值:"<<max<<endl;end=clock();printf("The time is %6.3f",(double)(end-start-over)/CLK_TCK);cout<<endl;cout<<endl;}}。

分治法练习题

分治法练习题

分治法练习题分治法是一种常见的算法设计方法,其核心思想是将问题划分成若干个规模较小且结构相似的子问题,然后分别解决这些子问题,最后将子问题的结果合并得到原问题的解。

在实际应用中,选取合适的问题划分方式以及合并子问题的结果是非常关键的。

下面,我将为您介绍两个分治法的练习题。

题目一:寻找最大子数组和给定一个整数数组,找到其连续子数组中的最大和。

例如,输入数组[-2, 1, -3, 4, -1, 2, 1, -5, 4],其最大子数组和为6,对应的子数组为[4, -1, 2, 1]。

解题思路:1. 将原问题划分成规模较小的子问题:将数组分为两部分,分别求解左子数组和右子数组的最大子数组和,以及跨越中点的最大子数组和。

2. 递归求解子问题:对于左右子数组,可以再次使用分治法求解;对于跨越中点的最大子数组和,可以通过以中点为中心,向左右扩展来得到。

3. 合并子问题的结果:对于左右子数组的最大子数组和,取较大值作为整个数组的最大子数组和;对于跨越中点的最大子数组和,取两边相加的最大值。

题目二:求解逆序对个数给定一个数组,逆序对是指数组中两个元素a[i]和a[j],满足i < j且a[i] > a[j]。

请设计一个算法,求解给定数组中逆序对的个数。

解题思路:1. 将原问题划分成规模较小的子问题:将数组平均分为两部分,分别求解左子数组和右子数组中逆序对的个数,以及两个子数组之间的逆序对个数。

2. 递归求解子问题:对于左右子数组,可以再次使用分治法求解;对于两个子数组之间的逆序对个数,可以通过归并排序的思想来求解。

3. 合并子问题的结果:将左右子数组合并为一个有序数组,并统计两个子数组之间的逆序对个数。

同时,递归返回的结果也需要累加进逆序对的总数。

通过以上两个练习题,我们可以更加深入地理解和应用分治法这一算法设计思想,同时也能提升对问题分解和结果合并的能力。

当然,在实际应用中,我们需要灵活运用分治法以及结合具体问题来设计合适的算法,并注意算法的效率和性能。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

7个乘法和10个加 (减)法
则:
C11 P S T V C12 P T C21 Q S C22 P R Q U
8个加(减)法
斯特拉森时间复杂度
b T (n) 2 7 T ( n / 2) an n2 n2
斯特拉森矩阵乘法目前只具有理论意义。 n2相当大时才会优于通常的矩阵乘法。 T (只有当 n) an (1 7 / 4 (7 / 4) 2 (7 / 4) k 1 ) 7 k T (1) (n≧120 2 ) log n log n
( (i 1)) (n 2 )
1
n
最坏情况时间是O(n)的选择算法
基本思想:精心挑选划分元素v 方法:二次取中间值 目的:使v比一部分元素小比另一部分元素大
使用二次取中规则的选择算法的说明性描述
public static int Select2(int a[],int k,int n) { //在集合a中找第k小元素 ① 若n≤r,则采用插入法直接对a分类并返回第k小元素。 ② 把a分成大小为r的个子集合,忽略剩余的元素。 ③ 设 M m1 , m2 ,, mn / r 是上面 n / r 个子集合的中间值的集合。 ④ v Select 2( M , / 2 n / r ) n / r , ⑤ 用Partition划分a,v作为划分元素。 ⑥ 假设v在位置j。 ⑦ case k=j: return(v);
最坏情况时间
Select的最坏情况时间是O( n 2 ) Select的平均情况时间是O(n)
最坏情况下的特例: 输入a恰好使对Partition的第i次调用选用的划分
元素是第i小元素,而k=n。 此时,(区间下界)m随
着Partition的每一次调用而仅增加1,j保持不变。
Partition最终需要调用n次。 则n次调用的时间总量是:
2) 如何保存n / r 个子集合的中间值 注:各组找到的中间元素值将调整到数组a的前 部,连续保存,从而可用递归调用的方式对这些中间 值进行排序并找出中间值的中间值。
Select2的实现
public static int Sel(int m,int p,int k) {//返回一个i,使得i属于[m,p],且a[i]是a[m:p]中第k小元素,r是一个全程变量,其取值为一 个大于1的整数。 int i,j,n,temp; if(p-m+1<=r) { InsertionSort(m,p); return(m+k-1);//返回第k小元素的位置 } while(true) { n=p-m+1; //元素数 for(i=1;i<=n/r;i++) //计算中间值 { InsertionSort(m+(i-1)*r,m+i*r-1); //将中间值收集到a[m:p]的前部 temp=a[m+i-1]; a[m+i-1]=a[m+(i-1)*r+r/2-1]; a[m+(i-1)*r+r/2-1]=temp; } j=Sel(m,m+n/r-1, ((n/r)+1)/2); //二次取中求mm temp=a[m]; a[m]=a[j]; a[j]=temp; //交换a[m]与a[j] j=Partition(m,p); if((j-m+1)==k) return(j); else if(j-m+1>k) p=j-1; else { k=k-(j-m+1); m=j+1; } } }


k<j: 设S是a[1:j-1]中元素的集合
return(Select2(S,k,j-1)) else:设R是a[j+1:n]中元素的集合 return(Select2(R,k-j,n-j))
}
Select2的待解决问题
算法中需要解决的两个问题 1) 如何确定子集合的中间值 当r较小时,采用InsertionSort(a,i,j)直接对每组 的r个元素分类,在分类好的序列中,中间元素即为当 前r个元素中的中间位置下标对应的元素。
cn (7 / 4)
7
cnlog 4 log 7 log 4 nlog 7 (c 1)nlog 7 (nlog 7 ) (n 2.81 )
斯特拉森矩阵乘法
设矩阵A和B是两个n×n矩阵,讨论矩阵加法的时间复 杂度和矩阵乘法的时间复杂度。 观察:矩阵乘法的花费比矩阵加法大 。 矩阵加法:Θ( n2 )
3 矩阵乘法:Θ( n)
C (i, j )
1k n
A(i, k )B(k , j)
1 i, j n
分治法解决矩阵乘法(降低计算复杂度)
利用Partition实现的选择算法
public static void Select(int n,int k) {//在数组a[1],…,a[n]中找第k小元素s并把它放在位置k,假设1≤k≤n。 //将剩下的元素按如下方式排列,使a[k]=t,对于1≤m<k,有a[m]≤t;对 于k<m≤n,有a[m]≥t。a[n+1]=+∞ int m,r,j; m=1;r=n+1;a[n+1]=10000; while(true) //每当进入这一循环时,1≤m≤k≤r≤n+1 { j=r; //将剩余元素的最大下标加1后给j j=Partition(m,j); //返回j,它使得a[j]是第j小的值 if(k<j) r=j; //j是新的上界 else if(k==j) return; else m=j+1; //j+1是新的下界 } }
斯特拉森矩阵乘法
假设:n=2k
A11 A 21
其中:
A12 B11 B A22 21
B12 C11 C12 B22 C21 C22
技巧:增加加法减少乘法
C11 A11B11 A12 B21 C12 A11 B12 A12 B22 C21 A21 B11 A22 B21 C22 A21 B12 A22 B22
利用Partition实现的选择算法实例分析
算法复Байду номын сангаас度分析
假设 ① a中的元素互异; ② 随机选取划分元素,且选择a[m:p-1]中任一元素作为划分 元素的概率相同。 分析 在Select中每次调用Partition(m,j),所需的元素比较次是 j-m+1。 在执行一次Partition后,或者找到第k小元素,或者将在缩 小的子集(a[m,k-1]或a[k+1,j])中继续查找。缩小的子集的元 素数将至少比上一次划分的元素数少1。
斯特拉森矩阵乘法的思想
令:
P ( A11 A22 )( B11 B22 ) Q ( A21 A22 ) B11 R A11 ( B12 B22 ) S A22 ( B21 B11 ) T ( A11 A12 ) B22 U ( A21 A11 )( B11 B12 ) V ( A12 A22 )( B21 B22 )
选择问题
1. 问题描述 在n个元素的表a[1:n]中确定第k小元素,1≤k≤n。 2. 设计思路 利用Partition过程。在第一次划分后划分元素v测定在a[j] 的位置上,则有j-1个元素小于或等于a[j],且有n-j个元素大 于或等于a[j]。此时, 若k=j,则a[j]即是第k小元素;否则, 若k<j,则a[1:n]中的第k小元素将出现在a[1:j-1]中, 且仍是a[1:j-1]中的第k小元素; 若k>j,则a[1:n]中的第k小元素将出现在a[j+1:n], 是a[j+1:n]中的第k-j小元素。
相关文档
最新文档