《冬训周报一》

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

例题

P8683 [蓝桥杯 2019 省 B] 后缀表达式

解题思路:因为输入的数字有正有反,如果没有负号直接将数组的数进行相加,有负号则先按大小进行排序,因为至少会减一次所以先用最大减去最小,然后其余数全取正数。

#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 的数。

例题

P8889 狠狠地切割(Hard Version)

因为数据大的原因单独去循环会导致时间不够,此时可以用二分来进行数的查找,这样做的好处在于,每次查找都可以帮我们排除一半的数,最多仅需 logn 次就可以找到 x 在不在数列中,以及是数列的第几项。

若对 ai进行二分查找,则我们定义三个变量 l,r,mid令 l=1r=nmid 为 l 和 r 的平均数,即 (l+r)/2

l 代表二分的左端点,r代表二分的右端点,而 mid 是数列最中间一个元素,将与 x进行比较。

我们做一个 while() 循环,循环条件是 l<=r,意思是还有 blbr 可能等于 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;
}

 

 


 

 

posted @ 2023-01-08 17:16  Ke_scholar  阅读(24)  评论(0编辑  收藏  举报