《冬训周报一》
next_permutation基本用法
next_permutation函数的功能是将数组中选定范围的数按照字典序进行全排列。
基本用法如下:
#include <iostream> #include <algorithm> using namespace std; int main() { int a[5]={2,5,1,3,8}; sort(a,a+5);//如果不排序则从当前状态开始进行全排列 do { for(int i=0;i<5;i++) cout<<a[i]<<" "; cout<<endl; }while(next_permutation(a,a+5)); //(a+m,a+n)指从数组下表为m到n-1的数进行全排列 return 0; }
在该题P8599 [蓝桥杯 2013 省 B] 带分数中可用全排列的方式枚举1-9中的数字进行排列,再进行验证就可以筛选出答案的种数。
二叉树分析及特点
- [ 1 ] :一个二叉树在第k层上最大结点数2^(k-1),k>=1;
- [ 2 ] : 一个深度为k且具有2^k -1个结点的二叉树称为满二叉树;
- [ 3 ] : 深度为k的,有n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号从1至n的结点一一对应时,称为完全二叉树;
- [ 4 ] : 当二叉树根节点从1开始时,当父节点为i时,其左儿子为2i,右儿子为2i+1。
例题
P8681 [蓝桥杯 2019 省 AB] 完全二叉树的权值
求二叉树深度
while(m) { m=m/2; k++; } //求二叉树的深度;
当深度为i的时候,第i层的结点数就应该从2^(i-1) 到2 ^i -1之间,而求2的i次方,我们可以借助math库函数里面的pow(2,i)来计算:
2^(i-1)是每一层节点开始的下标,2 ^i -1是每一层节点结束的下标 。
for(i=1; i<k; i++) { sum=0; ll t1=pow(2,i-1),t2=pow(2,i); for(j=t1; j<=t2-1; j++) sum+=a[j]; if(sum>maxx) { maxx=sum; flag=i; } }
后缀表达式
运用后缀表达式进行计算的具体做法:
建立一个栈S 。从左到右读表达式,如果读到操作数就将它压入栈S中,如果读到n元运算符(即需要参数个数为n的运算符)则取出由栈顶向下的n项按操作符运算,再将运算的结果代替原栈顶的n项,压入栈S中 。如果后缀表达式未读完,则重复上面过程,最后输出栈顶的数值则为结束。
例 : 6 5 2 3 + 8 * + 3 + *
先将前面的数字入栈
栈 :6 5 2 3
遇到 ” + ” 取栈顶的两个操作数做加法, 2 + 3 = 5 , 入栈
栈 :6 5 5
遇到 ” 8 ” 入栈
栈 :6 5 5 8
遇到 ” * ” 取栈顶的两个操作数做乘法, 5 * 8 = 40 , 入栈
栈 :6 5 40
遇到 ” + ” 取栈顶的两个操作数做加法, 5 + 40 = 45 , 入栈
栈 :6 45
遇到 ” 3 ” 入栈
栈 :6 45 3
遇到 ” + ” 取栈顶的两个操作数做加法, 45 + 3 = 48 , 入栈
栈 :6 48
遇到 ” * ” 取栈顶的两个操作数做加法, 6 * 48 = 288 , 入栈
栈 :288
例题
解题思路:因为输入的数字有正有反,如果没有负号直接将数组的数进行相加,有负号则先按大小进行排序,因为至少会减一次所以先用最大减去最小,然后其余数全取正数。
#include<bits/stdc++.h> using namespace std; int main() { int n,m; long long num[200010]; cin>>n>>m; for(int i=0;i<n+m+1;i++) { cin>>num[i]; } long long sum = 0; if(m == 0) { // 没有负号 for(int i=0;i<n+m+1;i++) sum += num[i]; } else { sort(num,num+n+m+1); // 从小到大排序 sum += num[n+m] - num[0]; // 用最大的减去最小的 for(int i=1;i<n+m;i++) sum += num[i]>0 ? num[i]:-num[i]; // 剩下的全部取正数 } cout<<sum<<endl; return 0; }
二分查找
首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。
通俗来说,也就是给定一个长度为 n 的有序数列(如升序)和一个将查找的数 x,将数列中最中间一个元素(称为 ai)与 x进行比较,若 x=ai,则数列中间的元素与 x的值相等。若 x>ai,则二分查找从 ai+1 至 an 的数,若 x>ai,则二分查找从 a1 至 ai-1 的数。
例题
因为数据大的原因单独去循环会导致时间不够,此时可以用二分来进行数的查找,这样做的好处在于,每次查找都可以帮我们排除一半的数,最多仅需 logn 次就可以找到 x 在不在数列中,以及是数列的第几项。
若对 ai进行二分查找,则我们定义三个变量 l,r,mid令 l=1,r=n,mid 为 l 和 r 的平均数,即 (l+r)/2。
l 代表二分的左端点,r代表二分的右端点,而 mid 是数列最中间一个元素,将与 x进行比较。
我们做一个 while()
循环,循环条件是 l<=r
,意思是还有 bl∼br 可能等于 x,若 l>r,则没有一个数 bj 可能等于 ai。
循环内部则在更新 mid的值后将 bmid 与 ai 比较。
如果两者相等,则 ai 将被“狠狠地切割”。
如果 bmid<ai,则 l=mid+1,查找区间缩小,仅剩原来的右半区。
否则 r=mid-1,查找区间同样缩小,仅剩原来的左半区。
将本部分写成函数,对本题而言更方便。
代码实现:
bool check(long long k) { long long l=1,r=m,mid; while(l<=r){ mid=(l+r)/2; if (k<b[mid])r=mid-1; else if(k>b[mid])l=mid+1; else return 1; } return 0; }