最大子段和三种求解方法
- 格式:doc
- 大小:33.00 KB
- 文档页数:5
一、选择题1、二分搜索算法是利用( A )实现的算法。
A、分治策略B、动态规划法C、贪心法D、回溯法2、下列不是动态规划算法基本步骤的是( A )。
A、找出最优解的性质B、构造最优解C、算出最优解D、定义最优解3、最大效益优先是( A )的一搜索方式。
A、分支界限法B、动态规划法C、贪心法D、回溯法4、在下列算法中有时找不到问题解的是( B )。
A、蒙特卡罗算法B、拉斯维加斯算法C、舍伍德算法D、数值概率算法5. 回溯法解旅行售货员问题时的解空间树是( A )。
A、子集树B、排列树C、深度优先生成树D、广度优先生成树6.下列算法中通常以自底向上的方式求解最优解的是( B )。
A、备忘录法B、动态规划法C、贪心法D、回溯法7、衡量一个算法好坏的标准是( C )。
A 运行速度快B 占用空间少C 时间复杂度低D 代码短8、以下不可以使用分治法求解的是( D )。
A 棋盘覆盖问题B 选择问题C 归并排序D 0/1背包问题9. 实现循环赛日程表利用的算法是( A )。
A、分治策略B、动态规划法C、贪心法D、回溯法10、下列随机算法中运行时有时候成功有时候失败的是( C )A 数值概率算法B 舍伍德算法C 拉斯维加斯算法D 蒙特卡罗算法11.下面不是分支界限法搜索方式的是( D )。
A、广度优先B、最小耗费优先C、最大效益优先D、深度优先12.下列算法中通常以深度优先方式系统搜索问题解的是( D )。
A、备忘录法B、动态规划法C、贪心法D、回溯法13.备忘录方法是那种算法的变形。
( B )A、分治法B、动态规划法C、贪心法D、回溯法14.哈弗曼编码的贪心算法所需的计算时间为( B )。
A、O(n2n)B、O(nlogn)C、O(2n)D、O(n)15.分支限界法解最大团问题时,活结点表的组织形式是( B )。
A、最小堆B、最大堆C、栈D、数组16.最长公共子序列算法利用的算法是( B )。
A、分支界限法B、动态规划法C、贪心法D、回溯法17.实现棋盘覆盖算法利用的算法是( A )。
动态规划-最⼤⼦段和2018-01-14 21:14:58⼀、最⼤⼦段和问题问题描述:给定n个整数(可能有负数)组成的序列a1,a2,...,an,求该序列的最⼤⼦段和。
如果所有整数都是负数,那么定义其最⼤⼦段和为0。
⽅法⼀、最⼤⼦段和的简单算法显然可以在O(n^2)的时间复杂度上完成这个问题。
但是是否可以对算法进⾏优化呢?答案是肯定的。
⽅法⼆、分治算法朴素的分法是⼆分,问题就是如何merge,在merge的时候,因为已经确定了会包含边界点,所以可以在O(n)的时间复杂度上完成merge 时的最⼤⼦段和。
因此分治公式是:T(n) = 2T(n/2)+O(n)根据主定理可以算得,分治算法的时间复杂度为O(nlgn)。
int maxSubSum(int[] a,int L,int R){if(L == R) return a[L] > 0 ? a[L] : 0;else {int mid = L + (R - L) / 2;int sumL = maxSubSum(a, L, mid);int sumR = maxSubSum(a, mid + 1, R);int tmpL = 0;int tmpR = 0;int sum = 0;for (int i = mid; i >=0 ; i--) {sum += a[i];if(sum>tmpL) tmpL = sum;}sum = 0;for (int i = mid+1; i <=R ; i++) {sum += a[i];if(sum>tmpR) tmpR = sum;}return Math.max(sumL,Math.max(sumR,tmpL+tmpR));}}⽅法三、动态规划这种两端都是变化的问题是很难优化的,最好可以让⼀端固定,这样就会⼤⼤简化分析难度。
于是将j暂时从原式⼦中提取出来,将剩下的命名为b[j]。
分治法最大子段和问题实例演示在计算机算法领域,分治法是一种常见的算法设计策略,它通过将问题拆分成更小的子问题来解决复杂的任务。
其中,最大子段和问题是分治法非常适用的一个经典案例。
本文将深入探讨分治法在最大子段和问题上的应用,并通过实例演示来帮助读者全面理解这一概念。
一、分治法简介分治法是一种将问题划分成更小、更易解决的子问题的算法设计策略。
通过递归地解决这些子问题,并将它们的解合并起来,最终得到原始问题的解。
这种策略通常包括三个步骤:分解原问题、解决子问题、合并子问题的解。
二、最大子段和问题最大子段和问题是指在一个给定数组中,找到一个具有最大总和的连续子段。
这个问题在实际中有着广泛的应用,比如股票交易中的最大收益、最大利润等。
通过分治法来解决最大子段和问题,可以更高效地找到最优解。
三、分治法在最大子段和问题上的应用分治法在解决最大子段和问题时,通常采用递归的方式。
具体来说,可以将给定数组划分成两个子数组,分别求解左右子数组的最大子段和,然后再考虑跨越中点的最大子段和。
将这些子问题的解合并起来,得到整个数组的最大子段和。
这样就能通过分治法在较短的时间内得到最优解。
四、实例演示假设有一个由整数组成的数组A,其内容为[-2, 1, -3, 4, -1, 2, 1, -5, 4]。
我来演示如何通过分治法来解决这个数组的最大子段和问题。
1. 将数组A划分成两个子数组:A[左] = [-2, 1, -3, 4] 和 A[右] = [-1, 2, 1, -5, 4]。
2. 分别求解左右子数组的最大子段和。
对于A[左],最大子段和为4;对于A[右],最大子段和为4。
3. 接下来,考虑跨越中点的最大子段和。
在这个例子中,跨越中点的最大子段和为6。
4. 将这些子问题的解合并起来,得到整个数组的最大子段和为6。
通过这个实例演示,我们可以清晰地看到分治法在解决最大子段和问题方面的优势,以及其简洁高效的算法思想。
五、个人观点和理解在我看来,分治法是一种非常值得深入学习和探讨的算法设计策略。
区间dp知识点总结区间动态规划以区间为基本单位,将区间问题分解为子区间问题,并通过子问题的优化解来求解原问题的最优解。
在区间动态规划中,我们通常会先对区间进行预处理,然后进行状态转移和最优解的计算,最终得出整个区间的最优解。
本文将介绍区间动态规划的基本概念、相关术语、常用技巧和应用场景,并以具体的例题进行解析,希望能够帮助读者更好地理解并掌握区间动态规划的相关知识。
一、基本概念1. 区间在区间动态规划中,区间通常是指一段连续的序列,可以是数组、字符串或其他数据结构。
如有一个长度为n的数组,通常我们可以将数组的某个子区间[i, j]表示为数组的一段连续元素,其中i和j分别为区间的左右边界。
2. 状态在区间动态规划中,状态通常用来表示问题的解空间,它是问题的一个关键要素。
状态的选择不同,可能会导致不同的算法解法。
3. 状态转移方程区间动态规划的核心是状态转移方程,它描述了问题的状态如何转移,以及如何通过子问题的最优解来求解原问题的最优解。
状态转移方程通常包括两个方面:状态之间的转移关系和状态的初始值。
4. 最优解区间动态规划通常是要求解区间范围内的最优解问题。
最优解可能是指区间的最大值、最小值、最长子序列等。
二、相关术语1. 最长上升子序列(Longest Increasing Subsequence,简称LIS)最长上升子序列是指一个序列中各个元素都严格递增的子序列,且该子序列的长度最大。
在区间动态规划中,求解最长上升子序列的长度是一个常见的问题。
2. 最大子段和(Maximum Subarray Sum)最大子段和是指一个序列中连续元素的和中最大的值。
在区间动态规划中,求解最大子段和也是一个常见的问题。
3. 背包问题背包问题是一类经典的组合优化问题,它包括 0-1 背包问题、多重背包问题、分组背包问题等。
在区间动态规划中,背包问题的求解也是一个重要的应用场景。
4. 字符串处理字符串处理是区间动态规划中一个重要的应用场景,常见的问题包括编辑距离、最长公共子序列、最长回文子串等。
#include <iostream>using namespace std;/*设dp[x] :以x位置处的数字为结尾,能够得到的最大子段和。
为按顺序枚举每个位置:对于每个数字有两种决策:自己和自己组成一个子段自己和前面的数字组成一个子段。
至于前面如何组成一个最大的子段则不需要关心。
所以,dp[x] := max(data[x], dp[x-1] + data[x])*/int* SavedDP;//备忘录法,以空间换时间int* FlagDP;enum KindStatus {SELF,CONNECTFRONT};void Print(int max_tail_index,int* data);int DoMaxSubSum_ReCur(int x, int* data){if(SavedDP[x] != INT_MIN) //查备忘录return SavedDP[x] ;int Res = 0;if(x == -1)return 0;int Sub1 = data[x];int Sub2 = DoMaxSubSum_ReCur(x-1,data) + data[x];if(Sub1 >= Sub2){FlagDP[x] = SELF;Res = Sub1;}else{FlagDP[x] = CONNECTFRONT;Res = Sub2;}SavedDP[x] = Res;//写备忘录return Res;}int DoMaxSubSum_DP(int* data,int n){int dp[100];dp[0] = data[0];for(int i = 1; i < n; i++){int Sub1 = data[i];int Sub2 = dp[i-1] + data[i];int Res;if(Sub1 >= Sub2){FlagDP[i] = SELF;Res = Sub1;}else{FlagDP[i] = CONNECTFRONT;Res = Sub2;}dp[i] = Res;}return 0;}int TotalDoMaxSubSum_ReCur(int* data,int n) {int max = INT_MIN;int max_tail_index = -1;for(int i = 0; i < n; i++){int temp = DoMaxSubSum_ReCur(i,data);if(max < temp){max = temp;max_tail_index = i;}}Print(max_tail_index,data);return max;}void Print(int max_tail_index,int* data){if( FlagDP[max_tail_index] == SELF){cout<<data[max_tail_index]<<" ";}else{Print(max_tail_index-1,data);cout<<data[max_tail_index]<<" ";}}int main(){int data[] = {4 ,-41, -1 ,2 ,2, 3 ,-3, 4 ,-4};int n = 15;SavedDP = new int[n];FlagDP = new int[n];for(int i = 0; i < n; i++){// data[i] = i+1;SavedDP[i] = INT_MIN;}cout<<TotalDoMaxSubSum_ReCur(data,8);delete[] SavedDP;delete[] FlagDP;}。
计算机算法设计与分析习题及答案一.选择题1、二分搜索算法是利用 A 实现的算法;A、分治策略B、动态规划法C、贪心法D、回溯法2、下列不是动态规划算法基本步骤的是 A ;A、找出最优解的性质B、构造最优解C、算出最优解D、定义最优解3、最大效益优先是A 的一搜索方式;A、分支界限法B、动态规划法C、贪心法D、回溯法4. 回溯法解旅行售货员问题时的解空间树是 A ;A、子集树B、排列树C、深度优先生成树D、广度优先生成树5.下列算法中通常以自底向上的方式求解最优解的是B ;A、备忘录法B、动态规划法C、贪心法D、回溯法6、衡量一个算法好坏的标准是 C ;A 运行速度快B 占用空间少C 时间复杂度低D 代码短7、以下不可以使用分治法求解的是 D ;A 棋盘覆盖问题B 选择问题C 归并排序D 0/1背包问题8. 实现循环赛日程表利用的算法是A ;A、分治策略B、动态规划法C、贪心法D、回溯法9.下面不是分支界限法搜索方式的是D ;A、广度优先B、最小耗费优先C、最大效益优先D、深度优先10.下列算法中通常以深度优先方式系统搜索问题解的是D ;A、备忘录法B、动态规划法C、贪心法D、回溯法11.备忘录方法是那种算法的变形; BA、分治法B、动态规划法C、贪心法D、回溯法12.哈夫曼编码的贪心算法所需的计算时间为B ;A、On2nB、OnlognC、O2nD、On13.分支限界法解最大团问题时,活结点表的组织形式是B ;A、最小堆B、最大堆C、栈D、数组14.最长公共子序列算法利用的算法是B;A、分支界限法B、动态规划法C、贪心法D、回溯法15.实现棋盘覆盖算法利用的算法是A ;A、分治法B、动态规划法C、贪心法D、回溯法16.下面是贪心算法的基本要素的是C ;A、重叠子问题B、构造最优解C、贪心选择性质D、定义最优解17.回溯法的效率不依赖于下列哪些因素 DA.满足显约束的值的个数B. 计算约束函数的时间C.计算限界函数的时间D. 确定解空间的时间18.下面哪种函数是回溯法中为避免无效搜索采取的策略BA.递归函数 B.剪枝函数 C;随机数函数 D.搜索函数19. D是贪心算法与动态规划算法的共同点;A、重叠子问题B、构造最优解C、贪心选择性质D、最优子结构性质20. 矩阵连乘问题的算法可由 B 设计实现;A、分支界限算法B、动态规划算法C、贪心算法D、回溯算法21. 分支限界法解旅行售货员问题时,活结点表的组织形式是 A ;A、最小堆B、最大堆C、栈D、数组22、Strassen矩阵乘法是利用A 实现的算法;A、分治策略B、动态规划法C、贪心法D、回溯法23、使用分治法求解不需要满足的条件是 A ;A 子问题必须是一样的B 子问题不能够重复C 子问题的解可以合并D 原问题和子问题使用相同的方法解24、下面问题 B 不能使用贪心法解决;A 单源最短路径问题B N皇后问题C 最小生成树问题D 背包问题25、下列算法中不能解决0/1背包问题的是 AA 贪心法B 动态规划C 回溯法D 分支限界法26、回溯法搜索状态空间树是按照 C 的顺序;A 中序遍历B 广度优先遍历C 深度优先遍历D 层次优先遍历27.实现合并排序利用的算法是A ;A、分治策略B、动态规划法C、贪心法D、回溯法28.下列是动态规划算法基本要素的是D ;A、定义最优解B、构造最优解C、算出最优解D、子问题重叠性质29.下列算法中通常以自底向下的方式求解最优解的是 B ;A、分治法B、动态规划法C、贪心法D、回溯法30.采用广度优先策略搜索的算法是A ;A、分支界限法B、动态规划法C、贪心法D、回溯法31、合并排序算法是利用 A 实现的算法;A、分治策略B、动态规划法C、贪心法D、回溯法32、背包问题的贪心算法所需的计算时间为 BA、On2nB、OnlognC、O2nD、On33.实现大整数的乘法是利用的算法C ;A、贪心法B、动态规划法C、分治策略D、回溯法34.0-1背包问题的回溯算法所需的计算时间为AA、On2nB、OnlognC、O2nD、On35.采用最大效益优先搜索方式的算法是A;A、分支界限法B、动态规划法C、贪心法D、回溯法36.贪心算法与动态规划算法的主要区别是B;A、最优子结构B、贪心选择性质C、构造最优解D、定义最优解37. 实现最大子段和利用的算法是B ;A、分治策略B、动态规划法C、贪心法D、回溯法38.优先队列式分支限界法选取扩展结点的原则是 C ;A、先进先出B、后进先出C、结点的优先级D、随机39.背包问题的贪心算法所需的计算时间为 B ;A、On2nB、OnlognC、O2nD、On40、广度优先是A 的一搜索方式;A、分支界限法B、动态规划法C、贪心法D、回溯法41. 一个问题可用动态规划算法或贪心算法求解的关键特征是问题的 B ;A、重叠子问题B、最优子结构性质C、贪心选择性质D、定义最优解42.采用贪心算法的最优装载问题的主要计算量在于将集装箱依其重量从小到大排序,故算法的时间复杂度为 B ;A 、On2nB 、OnlognC 、O2nD 、On43. 以深度优先方式系统搜索问题解的算法称为 D ;A 、分支界限算法B 、概率算法C 、贪心算法D 、回溯算法44. 实现最长公共子序列利用的算法是B ;A 、分治策略B 、动态规划法C 、贪心法D 、回溯法45. Hanoi 塔问题如下图所示;现要求将塔座A 上的的所有圆盘移到塔座B 上,并仍按同样顺序叠置;移动圆盘时遵守Hanoi 塔问题的移动规则;由此设计出解Hanoi 塔问题的递归算法正确的为:B46. 动态规划算法的基本要素为 CA. 最优子结构性质与贪心选择性质 B .重叠子问题性质与贪心选择性质C .最优子结构性质与重叠子问题性质 D. 预排序与递归调用 47. 能采用贪心算法求最优解的问题,一般具有的重要性质为: AA. 最优子结构性质与贪心选择性质 B .重叠子问题性质与贪心选择性质C .最优子结构性质与重叠子问题性质 D. 预排序与递归调用48. 回溯法在问题的解空间树中,按 D 策略,从根结点出发搜索解空间树;A.广度优先B. 活结点优先C.扩展结点优先D. 深度优先49. 分支限界法在问题的解空间树中,按 A 策略,从根结点出发搜索解空间树;A.广度优先B. 活结点优先C.扩展结点优先D. 深度优先50. 程序块 A 是回溯法中遍历排列树的算法框架程序;A.B. C. D. 51. 常见的两种分支限界法为DA. 广度优先分支限界法与深度优先分支限界法;B. 队列式FIFO 分支限界法与堆栈式分支限界法;C. 排列树法与子集树法;D. 队列式FIFO 分支限界法与优先队列式分支限界法;1.算法的复杂性有 时间 复杂性和 空间 ;2、程序是 算法用某种程序设计语言的具体实现;3、算法的“确定性”指的是组成算法的每条 指令 是清晰的,无歧义的;4. 矩阵连乘问题的算法可由 动态规划 设计实现;5、算法是指解决问题的 一种方法 或 一个过程 ;6、从分治法的一般设计模式可以看出,用它设计出的程序一般是 递归算法 ;7、问题的 最优子结构性质 是该问题可用动态规划算法或贪心算法求解的关键特征;8、以深度优先方式系统搜索问题解的算法称为 回溯法 ;9、计算一个算法时间复杂度通常可以计算 循环次数 、 基本操作的频率 或计算步; Hanoi 塔A. void hanoiint n, int A, int C, int B{ if n > 0{ hanoin-1,A,C, B;moven,a,b; hanoin-1, C, B, A; }} B. void hanoiint n, int A, int B, int C { if n > 0 { hanoin-1, A, C, B; moven,a,b; hanoin-1, C, B, A; } }C. void hanoiint n, int C, int B, int A { if n > 0 { hanoin-1, A, C, B; moven,a,b; hanoin-1, C, B, A; } }D. void hanoiint n, int C, int A, int B { if n > 0 { hanoin-1, A, C, B; moven,a,b; hanoin-1, C, B, A; } } void backtrack int t{ if t>n outputx; else for int i=t;i<=n;i++ { swapxt, xi; if legalt backtrackt+1; swapxt, xi; } } void backtrack int t { if t>n outputx;elsefor int i=0;i<=1;i++ { xt=i; if legalt backtrackt+1; } }void backtrack int t { if t>n outputx; else for int i=0;i<=1;i++ { xt=i; if legalt backtrackt-1; } }voidbacktrack int t{ if t>n outputx; else for int i=t;i<=n;i++ { swapxt, xi; if legalt backtrackt+1;}}10、解决0/1背包问题可以使用动态规划、回溯法和分支限界法,其中不需要排序的是动态规划 ,需要排序的是回溯法 ,分支限界法 ;11、使用回溯法进行状态空间树裁剪分支时一般有两个标准:约束条件和目标函数的界,N皇后问题和0/1背包问题正好是两种不同的类型,其中同时使用约束条件和目标函数的界进行裁剪的是 0/1背包问题 ,只使用约束条件进行裁剪的是 N皇后问题 ;12、贪心选择性质是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别;13、矩阵连乘问题的算法可由动态规划设计实现;14.贪心算法的基本要素是贪心选择性质和最优子结构性质 ;15. 动态规划算法的基本思想是将待求解问题分解成若干子问题 ,先求解子问题 ,然后从这些子问题的解得到原问题的解;16.算法是由若干条指令组成的有穷序列,且要满足输入、输出、确定性和有限性四条性质;17、大整数乘积算法是用分治法来设计的;18、以广度优先或以最小耗费方式搜索问题解的算法称为分支限界法 ;19、贪心选择性质是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别;20.快速排序算法是基于分治策略的一种排序算法;21.动态规划算法的两个基本要素是. 最优子结构性质和重叠子问题性质 ;22.回溯法是一种既带有系统性又带有跳跃性的搜索算法;23.分支限界法主要有队列式FIFO 分支限界法和优先队列式分支限界法;24.分支限界法是一种既带有系统性又带有跳跃性的搜索算法;25.回溯法搜索解空间树时,常用的两种剪枝函数为约束函数和限界函数 ;26.任何可用计算机求解的问题所需的时间都与其规模有关;27.快速排序算法的性能取决于划分的对称性 ;28.所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到 ;29.所谓最优子结构性质是指问题的最优解包含了其子问题的最优解 ;30.回溯法是指具有限界函数的深度优先生成法 ;31.用回溯法解题的一个显着特征是在搜索过程中动态产生问题的解空间;在任何时刻,算法只保存从根结点到当前扩展结点的路径;如果解空间树中从根结点到叶结点的最长路径的长度为hn,则回溯法所需的计算空间通常为 Ohn ;32.回溯法的算法框架按照问题的解空间一般分为子集树算法框架与排列树算法框架;33.用回溯法解0/1背包问题时,该问题的解空间结构为子集树结构;34.用回溯法解批处理作业调度问题时,该问题的解空间结构为排列树结构;35.旅行售货员问题的解空间树是排列树 ;三、算法填空1.背包问题的贪心算法void Knapsackint n,float M,float v,float w,float x{//重量为w1..n,价值为v1..n的 n个物品,装入容量为M的背包//用贪心算法求最优解向量x1..nint i; Sortn,v,w;for i=1;i<=n;i++ xi=0;float c=M;for i=1;i<=n;i++{if wi>c break;xi=1;c-=wi;}if i<=n xi=c/wi;}2.最大子段和: 动态规划算法int MaxSumint n, int a{int sum=0, b=0; //sum存储当前最大的bj, b存储bjfor int j=1; j<=n; j++{ if b>0 b+= aj ;else b=ai; ; //一旦某个区段和为负,则从下一个位置累和 ifb>sum sum=b;}return sum;}3.贪心算法求活动安排问题template<class Type>void GreedySelector int n, Type s, Type f, bool A{A1=true;int j=1;for int i=2;i<=n;i++if si>=fj{ Ai=true;j=i;}else Ai=false;}4.快速排序template<class Type>void QuickSort Type a, int p, int r{if p<r{int q=Partitiona,p,r;QuickSort a,p,q-1; //对左半段排序QuickSort a,q+1,r; //对右半段排序}}5. 回溯法解迷宫问题迷宫用二维数组存储,用'H'表示墙,'O'表示通道int x1,y1,success=0; //出口点void MazePathint x,int y{//递归求解:求迷宫maze从入口x,y到出口x1,y1的一条路径mazexy=''; //路径置为if x==x1&&y==y1 success=1; //到出口则成功else{if mazexy+1=='O' MazePathx,++y;//东邻方格是通路,向东尝试if success&&mazex+1y=='O' MazePath++x,y;//不成功且南邻方格是通路,向南尝试if success&&mazexy-1=='O' MazePathx,--y;//不成功且西邻方格是通路,向西尝试if success&&mazex-1y=='O' MazePath--x,y;//不成功且北邻方格是通路,向北尝试}if success mazexy=''; //死胡同置为}四、算法设计题1. 给定已按升序排好序的n个元素a0:n-1,现要在这n个元素中找出一特定元素x,返回其在数组中的位置,如果未找到返回-1;写出二分搜索的算法,并分析其时间复杂度;template<class Type>int BinarySearchType a, const Type& x, int n{//在a0:n中搜索x,找到x时返回其在数组中的位置,否则返回-1Int left=0; int right=n-1;While left<=right{int middle=left+right/2;if x==amiddle return middle;if x>amiddle left=middle+1;else right=middle-1;}Return -1;}时间复杂性为Ologn2. 利用分治算法写出合并排序的算法,并分析其时间复杂度void MergeSortType a, int left, int right{if left<right {//至少有2个元素int i=left+right/2; //取中点mergeSorta, left, i;mergeSorta, i+1, right;mergea, b, left, i, right; //合并到数组bcopya, b, left, right; //复制回数组a}}算法在最坏情况下的时间复杂度为Onlogn;3.N皇后回溯法bool Queen::Placeint k{ //检查xk位置是否合法for int j=1;j<k;j++if absk-j==absxj-xk||xj==xk return false;return true;}void Queen::Backtrackint t{if t>n sum++;else for int i=1;i<=n;i++{xt=i;if 约束函数 Backtrackt+1;}}4.最大团问题void Clique::Backtrackint i // 计算最大团{ if i > n { // 到达叶结点for int j = 1; j <= n; j++ bestxj = xj;bestn = cn; return;}// 检查顶点 i 与当前团的连接int OK = 1;for int j = 1; j < i; j++if xj && aij == 0 // i与j不相连{OK = 0; break;}if OK { // 进入左子树xi = 1; cn++;Backtracki+1;xi = 0; cn--; }if cn+n-i>bestn { // 进入右子树xi = 0;Backtracki+1; }}5. 顺序表存储表示如下:typedef struct{RedType rMAXSIZE+1; //顺序表int length; //顺序表长度}SqList;编写对顺序表L进行快速排序的算法;int PartitionSqList &L,int low,int high //算法10.6b{//交换顺序表L中子表L.rlow..high的记录,枢轴记录到位,并返回其所在位置, //此时在它之前后的记录均不大小于它.int pivotkey;L.r0=L.rlow; //用子表的第一个记录作枢轴记录pivotkey=L.rlow.key; //枢轴记录关键字while low<high //从表的两端交替地向中间扫描{while low<high&&L.rhigh.key>=pivotkey --high;L.rlow=L.rhigh; //将比枢轴记录小的记录移到低端while low<high&&L.rlow.key<=pivotkey ++low;L.rhigh=L.rlow; //将比枢轴记录大的记录移到高端}L.rlow=L.r0; //枢轴记录到位return low; //返回枢轴位置}void QSortSqList &L,int low,int high{//对顺序表L中的子序列L.rlow..high作快速排序int pivotloc;if low<high //长度>1{pivotloc=PartitionL,low,high; //将L.rlow..high一分为二QSortL,low,pivotloc-1; //对低子表递归排序,pivotloc是枢轴位置 QSortL,pivotloc+1,high; //对高子表递归排序}}void QuickSortSqList &L{//对顺序表L作快速排序QSortL,1,L.length; }。
动态规划——最⼤⼦段和⼀、最⼤⼦段和问题给定N个数A1, A2, ... An,从中选出k(k不固定)个连续的数字 Ai, Ai+1, ... Ai+k-1,使得∑i+k−1iAt 达到最⼤,求该最⼤值。
分析求最⼤⼦段和可以⽤多种算法来解决.(1)直接枚举max = 0;for i in [1...n]for j in [i....n]sum = 0;for k in [i...j]sum += A[k]if(sum > max)max = sum//时间复杂度为O(n^3)(2)求 sum[i...j]时,直接利⽤ sum[i...j] = sum[i...j-1] + A[j]来优化max = 0;for i in [1...n]sum = 0for j in [i....n]sum += A[j]if(sum > max)max = sum//时间复杂度为O(n^2)(3)分治法将A1...An⽤⼆分法分为左右两边,则A1...An中的最⼤连续⼦段和可能为三种情况:【1】是A1...An/2中的最⼤连续⼦段和【2】是An/2+1....An中的最⼤连续⼦段和【3】横跨左右两边int MaxSum(int* a, int beg, int end){if (beg == end){return a[beg] > 0? a[beg] :0;}int mid = (beg + end) / 2;int max_left = MaxSum(a, beg, mid);int max_right = MaxSum(a, mid + 1 ,end);int s1 = 0, s2 = 0, m_left = 0, m_right = 0;for(int i = mid; i <= beg; i --){s1 += a[i];if(s1 > m_left)m_left = s1;}for(int i = mid+1; i <= end; i ++){s2 += a[i];if(s2 > m_right)m_right = s2;}int max_sum = max_left;if(max_right > max_sum)max_sum = max_right;if(m_right + m_left > max_sum)max_sum = m_left + m_right;return max_sum;}//时间复杂度为 O(nlogn)(4)动态规划算法⽤动归数组 dp[i]表⽰以Ai结尾的若⼲个连续⼦段的和的最⼤值,则有递推公式:dp[i] = max{dp[i-1] + A[i], A[i]}int max = 0;for(int i = 1; i <= n; i ++){if(dp[i-1] > 0){dp[i] = dp[i-1] + A[i];}else{dp[i] = A[i];}if(dp[i]> max){max = dp[i];}}//时间复杂度为O(n)⼆、最⼤⼦矩阵和问题给定MxN的矩阵,其⼦矩阵R{x1, y1, x2, y2} (x1, y1) 为矩阵左上⾓的坐标,(x2, y2)为矩阵右下⾓的坐标,S(x1,y1,x2,y2)表⽰⼦矩阵R中的数字的和,求所有⼦矩阵的和的最⼤值。
三种⽅法求解最⼤⼦区间和:DP、前缀和、分治题⽬洛⾕:LeetCode:给出⼀个长度为n的序列a,选出其中连续且⾮空的⼀段使得这段和最⼤。
挺经典的⼀道题⽬,下⾯分别介绍O(n) 的 DP 做法、前缀和做法,以及O(n log n) 的分治做法。
DP 做法⽤d i表⽰结尾为位置i的最⼤区间和,则有d i=max(d i−1,0)+a i问题的答案即为max{d i∣i∈[1,n]}。
编写代码时不需要开d数组,⽤变量 last_d 记录d i−1,变量 ans 记录max{d i},并在扫描时动态更新即可。
时间复杂度O(n),空间复杂度O(1)。
核⼼代码如下:maxn = int(2e5 + 5)arr = [0 for _ in range(maxn)] # 从下标 1 开始存# 输⼊过程略……ans = Nonelast_d = 0for i in range(1, n + 1):temp_ans = max(last_d, 0) + arr[i]if ans is None or temp_ans > ans:ans = temp_anslast_d = temp_ansprint(ans)前缀和做法将数列前n项的和记为sum n:sum n=n ∑i=1a i可以⽤前缀和快速求区间和:y∑i=x a i=sum y−sum x−1⽤d i表⽰结尾为位置i的最⼤区间和,则有d i=sum i−min{sum j∣j<i}问题的答案即为max{d i∣i∈[1,n]}。
编写代码时只需要开前缀和数组,⽆需开d数组,⽤变量 cur_min_pre_sum 记录min{sum j},变量 ans 记录max{d i},并动态维护即可。
时间复杂度O(n),空间复杂度O(n)。
核⼼代码如下:maxn = int(2e5 + 5)arr = [0 for _ in range(maxn)] # 原数组,从下标 1 开始存pre_sum = [0 for _ in range(maxn)] # 前缀和数组# 输⼊过程略……# 预处理前缀和for i in range(1, n + 1):pre_sum[i] = pre_sum[i - 1] + arr[i]cur_min_pre_sum = 0ans = Nonefor i in range(1, n + 1):temp_ans = pre_sum[i] - cur_min_pre_sumif ans is None or temp_ans > ans:ans = temp_anscur_min_pre_sum = min(cur_min_pre_sum, pre_sum[i])print(ans)分治做法若有⼀区间 [start,stop),区间中点为mid,其最⼤⼦段和对应的⼦区间为 [i,j),则 [i,j) 只有以下三种情况:[i,j) 完全在左⼦区间 [start,mid) 内;[i,j) 完全在右⼦区间 [mid,stop) 内;[i,j) 横跨中点mid。
…,AN, find the maximum value.代表 64 位有符号整数,记录程序运行时可以获得当前的处理器的频率分治法求最大子段和问题共有四种方法:算法一; 算法二;算法三、 Divide and Conquer 算法四源代码、 On-line Algorithm算法一源代码:/*Given (possibly negative) integers A1, A2, 最大子段和 */#include<stdio.h> #include <time.h> #include <windows.h>int MaxSubsequenceSum(int A[],int N); main(){int i,N,*A,MaxSum,judge;LARGE_INTEGER begin,end,frequency; // 间 QueryPerformanceFrequency(&frequency);// printf(" 输入整数的个数: "); scanf("%d",&N);A=(int *)malloc(N*sizeof(int)); // 用数组给数据动态分配空间printf(" 自行输入数据请按 1,随机产生数据请按 2\n");scanf("%d",&judge); if(judge==1){ // 自行输入数据printf("输入 %d (整数:”,N);for(i=0;i<N;i++)scanf("%d",&A[i]); } else{ printf (”随机产生的%介整数为:\n",N); // 用随机种子随机产生 N 个0-999整数srand(time(0)); for (i=0;i<N;i++){if(rand()%2==0) // 利用随机数奇偶性的等概率使得正负数约各占一半A[i]=rand()%1000; elseA[i]=(-1)*rand()%1000; printf("%d\t",A[i]);}int MaxSubsequenceSum(int A[],int N) {int ThisSum,MaxSum=0,i,j,k; for(i=0;i<N;i++)// for(j=i;j<N;j++){ //ThisSum=0; for(k=i;k<=j;k++) ThisSum+=A[k]; // if(ThisSum>MaxSum) MaxSum=ThisSum; // }return MaxSum;}//对最大值初始化从 A[i] 开始 以 A[j] 结束从 A[i] 到 A[j] 求和 更新最大值算法 2 源代码:/*Given (possibly negative) integers A1, A2, 最大子段和 */#include<stdio.h> #include <time.h> #include <windows.h>int MaxSubsequenceSum(int A[],int N); main(){int i,N,*A,MaxSum,judge;LARGE_INTEGER begin,end,frequency; // 时间QueryPerformanceFrequency(&frequency);// printf(" 输入整数的个数: "); scanf("%d",&N);A=(int *)malloc(N*sizeof(int)); //…,AN, find the maximum value.代表 64 位有符号整数,记录程序运行可以获得当前的处理器的频率用数组给数据动态分配空自行输入数据QueryPerformanceCounter(&begin); // 记录算法 1 开始时间MaxSum=MaxSubsequenceSum(A,N); QueryPerformanceCounter(&end); // 记录算法 1 终止时间 printf("\n printf(" 最大子段和为: %d\n",MaxSum); 算 法 1 运 行 时 间 为 :%f seconds\n",(double)(end.QuadPart-begin.QuadPart)/frequency.QuadPart);// 终止时间 -开始时间 =算法 1 运行时间system("pause");}printf(" 自行输入数据请按 1,随机产生数据请按 2\n"); scanf("%d",&judge); if(judge==1){ //int MaxSubseque nceSu m(int A[],int N) {intThisSum,////对最大值初始化从开始以A[j] 结束从A[i] 到A[j] 求和更新最大值…,AN, find the maximum value. 找printf(" 输入%d 个整数:",N);for(i=0;i<N;i++)scanf("%d",&A[i]);}else{printf(" 随机产生的%d 个整数为:\n",N); // 用随机种子随机产生N 个0-999 的整数srand(time(0));for (i=0;i<N;i++){if(rand()%2==0) // 利用随机数奇偶性的等概率使得正负数约各占一半A[i]=rand()%1000;elseA[i]=(-1)*rand()%1000;printf("%d\t",A[i]);}}QueryPerformanceCounter(&begin); // 记录算法 2 开始时间MaxSum=MaxSubsequenceSum(A,N);QueryPerformanceCounter(&end); // 记录算法 2 终止时间printf("\n 最大子段和为:%d\n",MaxSum);printf(" 算法 2 运行时间为:%fseconds\n",(double)(end.QuadPart-begin.QuadPart)/frequency.QuadPart); // 终止时间- 开始时间=算法2运行时间system("pause");}算法三源代码:分治法/*Given (possibly negative) integers A1, A2, 最大子段和*/#include<stdio.h>#include <time.h>#include <windows.h>int Divide_Conquer(int a[],int left,int right);main(){int i,N,*A,MaxSum,judge;LARGE_INTEGER begin,end,frequency; // 代表64 位有符号整数,记录程序运行时间QueryPerformanceFrequency(&frequency);// 可以获得当前的处理器的频率printf(" 输入整数的个数:");scanf("%d",&N);A=(int *)malloc(N*sizeof(int)); // 用数组给数据动态分配空间printf(" 自行输入数据请按1,随机产生数据请按2\n"); scanf("%d",&judge);if(judge==1){ // 自行输入数据printf(" 输入%d 个整数:",N);for(i=0;i<N;i++) scanf("%d",&A[i]);}else{printf(" 随机产生的%d 个整数为:\n",N); // 用随机种子随机产生N 个0-999 的整数srand(time(0));for (i=0;i<N;i++){if(rand()%2==0) // 利用随机数奇偶性的等概率使得正负数约各占一半A[i]=rand()%1000;elseA[i]=(-1)*rand()%1000; printf("%d\t",A[i]);}}QueryPerformanceCounter(&begin); // 记录算法3 开始时间MaxSum=Divide_Conquer(A,0,N);QueryPerformanceCounter(&end); // 记录算法 3 终止时间printf("\n 最大子段和为:%d\n",MaxSum);printf(" 算法 3 运行时间为:%fseconds\n",(double)(end.QuadPart-begin.QuadPart)/frequency.QuadPart); // 终止时间- 开始时间=算法3运行时间system("pause");}int Divide_Conquer(int a[],int left,int right)// 分治算法{int sum=0,leftsum=0,rightsum=0,center,i; if(left==right){if(a[left]>0) sum=a[left];elsesum=0;}else{center=(left+right)/2; // 将数据一分为 2 leftsum=Divide_Conquer(a,left,center); // 递归求左边区间的数据的最大子段rightsum=Divide_Conquer(a,center+1,right); // 递归求右边区间的数据的最大子段int s1=0,lefts=0;for(i=center;i>=left;i--){ // lefts+=a[i]; if(lefts>=s1) s1=lefts;}int s2=0,rights=0;for(i=center+1;i<=right;i++){//rights+=a[i]; if(rights>=s2)s2=rights;}sum=s1+s2; 求左边区间的数据的最大子段求右边区间的数据的最大子段if(sum<leftsum) //sum=leftsum;if(sum<rightsum)sum=rightsum;}return sum;}最大值更新算法四源代码:On-line Algorithm /*Given(possibly negative) integers A1, A2, 最大子段和*/#include<stdio.h>#include <time.h>#include <windows.h>int MaxSubsequenceSum(int A[],int N);main(){…,AN, find the maximum value.代表 64 位有符号整数,记录程序运行时可以获得当前的处理器的频率 int i,N,*A,MaxSum,judge;LARGE_INTEGER begin,end,frequency; //间QueryPerformanceFrequency(&frequency);// printf(" 输入整数的个数: "); scanf("%d",&N); A=(int *)malloc(N*sizeof(int)); //printf (" 自行输入数据请按 1,随机产生数据请按 2\n");scanf("%d",&judge);if(judge==1){ // 自行输入数据 printf(" 输入 %d 个整数: ",N); for(i=0;i<N;i++)scanf("%d",&A[i]); } else{ printf ("随机产生的 %d 个整数为 :\n",N ); // 用随机种子随机产生 N 个 0-999的整数srand(time(0)); for (i=0;i<N;i++){if(rand()%2==0) // 利用随机数奇偶性的等概率使得正负数约各占一半A[i]=rand()%1000; elseA[i]=(-1)*rand()%1000; printf("%d\t",A[i]);}}QueryPerformanceCounter(&begin); // 记录算法 4 开始时间MaxSum=MaxSubsequenceSum(A,N); QueryPerformanceCounter(&end); // 记录算法 4 终止时间printf("\n 最大子段和为: %d\n",MaxSum); printf(" 算 法 4 运 行 时 间 为 :%fseconds\n",(double)(end.QuadPart-begin.QuadPart)/frequency.QuadPart);// 终止时间 -开始时间 =算法 1 运行时间system("pause");}int MaxSubsequenceSum(int A[],int N){int ThisSum,MaxSum,j; ThisSum=0;MaxSum=0; // 对最大值初始化 for(j=0;j<N;j++){ // 从 A[j] 开始ThisSum += A[j]; // 从 A[i] 到 A[j] 求和 if(ThisSum>MaxSum)MaxSum=ThisSum; // 更新最大值 else if(ThisSum<0)ThisSum=0; // 若和为负,则为 0 ,舍去 }return MaxSum;}欢迎您的下载,资料仅供参考!致力为企业和个人提供合同协议,策划案计划书,学习资料等等打造全网一站式需求。
1、最大子段和问题的简单算法:
代码:
#include<iostream>
using namespace std;
int MaxSum(int a[],int n,int &besti,int &bestj){
int sum=0;
int i,j,k;
for(i=1;i<=n;i++)
for(j=i;j<=n;j++)
{
int thissum=0;
for(k=i;k<=j;k++)thissum+=a[k];
if(thissum>sum){
sum=thissum;
besti=i;
bestj=j;
}
}
return sum;
}
int main(){
int n,a[100],m,i,j,maxsum;
cout<<"请输入整数序列的元素个数n:"<<endl;
cin>>n;
cout<<"请输入序列中各元素的值a[i](一共"<<n<<"个)"<<endl;
//for(m=1;m<=n;i++)
//cin>>a[m];
for(m=0;m<n;m++)
cin>>a[m];
int b[100];
for(m=0;m<n;m++)
b[m+1]=a[m];
maxsum=MaxSum(b,n,i,j);
cout<<"整数序列的最大子段和是:"<<maxsum<<endl;
cout<<"besti="<<i<<endl;
cout<<"bestj="<<j<<endl;
system("pause");
}
此算法的时间复杂度:O(n3)。
可对此算法进行适当改进,使其时间复杂度变为:O(n2)。
代码:
#include<iostream>
using namespace std;
int MaxSum(int a[],int n,int &besti,int &bestj){ int sum=0;
int i,j,k;
for(i=1;i<=n;i++){
int thissum=0;
for(j=i;j<=n;j++)
{
thissum+=a[j];
if(thissum>sum){
sum=thissum;
besti=i;
bestj=j;
}
}
}
return sum;
}
int main(){
int n,a[100],m,i,j,maxsum;
cout<<"请输入整数序列的元素个数n:"<<endl;
cin>>n;
cout<<"请输入序列中各元素的值a[i](一共"<<n<<"个)"<<endl;
//for(m=1;m<=n;i++)
//cin>>a[m];
for(m=0;m<n;m++)
cin>>a[m];
int b[100];
for(m=0;m<n;m++)
b[m+1]=a[m];
maxsum=MaxSum(b,n,i,j);
cout<<"整数序列的最大子段和是:"<<maxsum<<endl;
cout<<"besti="<<i<<endl;
cout<<"bestj="<<j<<endl;
system("pause");
}
2、最大子段和问题的分治法:T(n)=O(nlog(n))。
//最大子段和,分治算法。
T(n)=O(nlog(n))。
#include<iostream>
using namespace std;
int MaxSubSum(int a[],int left,int right){
int sum=0;
if(left==right)sum=a[left]>0?a[left]:0;
else{
int center=(left+right)/2;
int leftsum=MaxSubSum(a,left,center);
int rightsum=MaxSubSum(a,center+1,right);
int s1=0;
int lefts=0;
for(int i=center;i>=left;i--){
lefts+=a[i];
if(lefts>s1)s1=lefts;
}
int s2=0;
int rights=0;
for(int i=center+1;i<=right;i++){
rights+=a[i];
if(rights>s2)s2=rights;
}
sum=s1+s2;
if(sum<leftsum)sum=leftsum;
if(sum<rightsum)sum=rightsum;
}
return sum;
}
int main(){
int n,a[100],m,maxsum;
cout<<"请输入整数序列的元素个数n:"<<endl;
cin>>n;
cout<<"请输入序列中各元素的值a[i](一共"<<n<<"个)"<<endl; for(m=0;m<n;m++)
cin>>a[m];
int b[100];
for(m=0;m<n;m++)
b[m+1]=a[m];
maxsum=MaxSubSum(b,1,n);
cout<<"整数序列的最大子段和是:"<<maxsum<<endl;
system("pause");
}
3 最大子段和问题的动态规划算法:T(n)=O(n)。
#include<iostream>
using namespace std;
int MaxSum(int n,int a[]){
int sum=0;
int b=0;
for(int i=1;i<=n;i++){
if(b>0)b+=a[i];
else b=a[i];
if(b>sum)sum=b;
}
return sum;
}
int main(){
int n,a[100],m,maxsum;
cout<<"请输入整数序列的元素个数n:"<<endl;
cin>>n;
cout<<"请输入序列中各元素的值a[i](一共"<<n<<"个)"<<endl; for(m=0;m<n;m++)
cin>>a[m];
int b[100];
for(m=0;m<n;m++)
b[m+1]=a[m];
maxsum=MaxSum(n,b);
cout<<"整数序列的最大子段和是:"<<maxsum<<endl; system("pause");
}。