算法注意---2、遇到算法里面一些不懂的代码怎么办
一、总结
一句话总结:
代码有疑问的位置,带点实例在程序里面debug一下,代码每句的意思就异常清楚了
1、枚举法的核心关键?
对于枚举法,弄清楚枚举变量、枚举范围、枚举判断条件之后,代码非常好写,结合算法思路,的确非常棒
2、枚举非空数字连续子段的时候,因为我们的设定是起点终点都包含了,所以就算是非空段,终点也不是起点加1,而是起点?
终点也不是起点加1,而是起点,这样终点和起点一样的时候,字串就是1个
枚举变量:每一段的起点、终点
枚举范围:起点:1-n,终点:起点 - n
枚举判断条件:
求和得到每一段的和,在这些和里面选出最大的
3、C++ min,max所在头文件?
C++ min,max所在头文件 algorithm
4、遇到算法里面一些不懂的代码怎么办?
代码有疑问的位置,带点实例在程序里面debug一下,代码每句的意思就异常清楚了
5、分治(二分、递归)为什么可以求最大连续子序列的和(为什么可以枚举所有解)?
求解的过程也就是求解下面1、2、3三种情况,情况2是要求的,情况1和3由递归的终止条件可以得到最初解
①完全处于序列的左半:l<=i<=j<=mid ②完全处于序列的右半:mid<=i<=j<=r ③跨越序列中间:i<=mid<=j<=r 这就是分治为什么在函数主体中只进行了第二种情况的计算 分治在缩小范围的过程中,必然转化为第二种情况 不是这样,而是转化成1/2/3三种情况, 情况2要求,情况1和3由递归的终止条件可以得到最初解 //分治求最大连续子序列的和的时候 #include <iostream> #include <algorithm> using namespace std; int a[200005]; //分治(二分)求最大连续子序列的和 int find(int l,int r){ if(l==r) return a[l]; int mid=(l+r)/2; //1、计算第二种跨越mid情况的序列的最大和 //a、求以mid为尾的子序列的最大和 int maxx1=-0x7fffffff; int sum1=0; for(int k=mid;k>=l;k--){ sum1+=a[k]; maxx1=max(sum1,maxx1); } //b、求以mid为头的子序列的最大和 int maxx2=-0x7fffffff; int sum2=0; for(int k=mid;k<=r;k++){ sum2+=a[k]; maxx2=max(sum2,maxx2); } //2、比较方式1、2、3的最大值 return max(max(find(l,mid),find(mid+1,r)),maxx1+maxx2-a[mid]); } int main(){ int n; cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; } cout<<find(1,n)<<endl; return 0; }
6、分治一般用递归,递归里面真正的核心代码就是递推表达式?
a、无论是分治里面的二分查找,核心代码是判断查找,然后左右递归
b、还是分治做求最大连续子序列的和,核心代码是递归的递推表达式,也就是比较方式1、2、3的最大值,左边和右边的可以递归算出,中间的只能自己算出
①完全处于序列的左半:l<=i<=j<=mid ②完全处于序列的右半:mid<=i<=j<=r ③跨越序列中间:i<=mid<=j<=r //分治求最大连续子序列的和的时候 #include<cstdio> int n , arr[200200]; //arr存储该序列 const int minn = -19260817; // 定义最小值 inline int Max( int a , int b) { return a > b ? a : b ;} //自定义 Max 函数(好像比stl的快一点) int rec( int l , int r ) { //分治函数 if ( l == r ) { // l=r时,直接返回该位置的值 return arr[l]; } int mid = ( l + r )/2; int sum = 0 , ret1 = minn , ret2 = minn; //ret1为[l..mid]区间内包含mid的最大子段和,ret2为[mid+1..r]区间内包含(mid+1)的最大子段和 for( int i = mid ; i >= l ; i-- ) { //就是普普通通的找最大的代码 //而且找的是以mid结尾的串的最大和 sum += arr[i]; ret1 = Max( ret1 , sum ); } //求出[1..mid]区间最大值 sum = 0; for( int i = mid+1 ; i <= r ; i++ ) { //就是普普通通的找最大的代码 //这个显然是找的以mid+1开头的序列的最大和 sum += arr[i]; ret2 = Max( ret2 , sum ); } //求出[mid+1..r]区间最大值 return Max( Max( rec( l , mid ) , rec( mid + 1 , r ) ) , ret1 + ret2 ); //返回可能一 可能二 可能三 中的最大值 }
7、递归的终止条件和递归的递推表达式在递归中的真正作用(分治做递归理解)?
递归的终止条件其实是在配合递推表达式的基础上,给向左向右的递归提供初始值,
递归的递推表达式其实是递归中的核心代码,一般都特别少(一般一个式子)
8、递归做分治注意点:求最大连续子序列的和?
a、递归的终止条件:因为我们的递归是为了求l到r序列的子序列的最大值,所以当区间只有一个元素时,就是终止条件,那个元素就是子序列的最大值
b、递归的递推表达式:比较方式1、2、3的最大值,而第2种跨越mid值的需要我们去计算,1,3种到了递归的终止条件,自然有解
c、递归的返回值:子序列的最大值
①完全处于序列的左半:l<=i<=j<=mid ②完全处于序列的右半:mid<=i<=j<=r ③跨越序列中间:i<=mid<=j<=r //分治求最大连续子序列的和的时候 #include<cstdio> int n , arr[200200]; //arr存储该序列 const int minn = -19260817; // 定义最小值 inline int Max( int a , int b) { return a > b ? a : b ;} //自定义 Max 函数(好像比stl的快一点) int rec( int l , int r ) { //分治函数 if ( l == r ) { // l=r时,直接返回该位置的值 return arr[l]; } int mid = ( l + r )/2; int sum = 0 , ret1 = minn , ret2 = minn; //ret1为[l..mid]区间内包含mid的最大子段和,ret2为[mid+1..r]区间内包含(mid+1)的最大子段和 for( int i = mid ; i >= l ; i-- ) { //就是普普通通的找最大的代码 //而且找的是以mid结尾的串的最大和 sum += arr[i]; ret1 = Max( ret1 , sum ); } //求出[1..mid]区间最大值 sum = 0; for( int i = mid+1 ; i <= r ; i++ ) { //就是普普通通的找最大的代码 //这个显然是找的以mid+1开头的序列的最大和 sum += arr[i]; ret2 = Max( ret2 , sum ); } //求出[mid+1..r]区间最大值 return Max( Max( rec( l , mid ) , rec( mid + 1 , r ) ) , ret1 + ret2 ); //返回可能一 可能二 可能三 中的最大值 }
9、包含mid的子序列很好求?
即求出区间[i..mid]的最大值maxx1与区间[mid..j]的最大值maxx2,将其合并即可,即maxx1+maxx2-a[mid]
其中,很容易求出第二种情况,第二种情况也就是包含mid的子序列,
也就是[i...mid...j],而求[i...mid...j]的最大值,
即求出区间[i..mid]的最大值与区间[mid..j]的最大值,将其合并即可。
10、求最长子序列的和,为什么可以用分治?
其实无论是分左分右,还是包含mid的整个,所有的区间都会被枚举到
①完全处于序列的左半:l<=i<=j<=mid
②完全处于序列的右半:mid<=i<=j<=r
③跨越序列中间:i<=mid<=j<=r
二、内容在总结中
博客对应课程的视频位置: