• 2014年第五届蓝桥杯C/C++程序设计本科B组决赛


    1.年龄巧合(枚举)

    2.出栈次序(推公式/Catalan数)

    3.信号匹配(kmp)

    4.生物芯片(完全平方数)

    5.Log大侠(线段树)

    6.殖民地

    1.年龄巧合

    小明和他的表弟一起去看电影,有人问他们的年龄。小明说:今年是我们的幸运年啊。我出生年份的四位数字加起来刚好是我的年龄。表弟的也是如此。已知今年是2014年,并且,小明说的年龄指的是周岁。

    请推断并填写出小明的出生年份。

    答案:1988

    #include<iostream>
    #include<stdio.h>
    using namespace std;
    
    int main(){
    
        int a,b,c,d;
        int y;
    
        for(a=0;a<=2;++a){
            for(b=0;b<=9;++b){
                for(c=0;c<=9;++c){
                    for(d=0;d<=9;++d){
                        y=a*1000+b*100+c*10+d;
                        if( (2014-y)==(a+b+c+d) ){
                            printf("%d%d%d%d
    ",a,b,c,d);
                        }
                    }
                }
            }
        }
    
        return 0;
    }
    View Code

    ps:很尴尬,求出来了2个。。

    2.出栈次序

    X星球特别讲究秩序,所有道路都是单行线。一个甲壳虫车队,共16辆车,按照编号先后发车,夹在其它车流中,缓缓前行。
    路边有个死胡同,只能容一辆车通过,是临时的检查站,如图【p1.png】所示。
    X星球太死板,要求每辆路过的车必须进入检查站,也可能不检查就放行,也可能仔细检查。
    如果车辆进入检查站和离开的次序可以任意交错。那么,该车队再次上路后,可能的次序有多少种?
    为了方便起见,假设检查站可容纳任意数量的汽车。
    显然,如果车队只有1辆车,可能次序1种;2辆车可能次序2种;3辆车可能次序5种。

    现在足足有16辆车啊,亲!需要你计算出可能次序的数目。

    题意:求n个元素的出栈情况有多少种。

    思路:

    方法一:

    我们把n个元素的出栈个数的记为f(n), 那么对于1,2,3, 我们很容易得出:

                                        f(1)= 1     //即 1

                                        f(2)= 2     //即 12、21

                                        f(3)= 5     //即 123、132、213、321、231

    然后我们来考虑f(4), 我们给4个元素编号为a,b,c,d, 那么考虑:元素a只可能出现在1号位置,2号位置,3号位置和4号位置(很容易理解,一共就4个位置,比如abcd,元素a就在1号位置)。

    分析:

     1) 如果元素a在1号位置,那么只可能a进栈,马上出栈,此时还剩元素b、c、d等待操作,就是子问题f(3);

     2) 如果元素a在2号位置,那么一定有一个元素比a先出栈,即有f(1)种可能顺序(只能是b),还剩c、d,即f(2),    根据乘法原理,一共的顺序个数为f(1)* f(2);

     3) 如果元素a在3号位置,那么一定有两个元素比1先出栈,即有f(2)种可能顺序(只能是b、c),还剩d,即f(1),

    根据乘法原理,一共的顺序个数为f(2) * f(1);

     4) 如果元素a在4号位置,那么一定是a先进栈,最后出栈,那么元素b、c、d的出栈顺序即是此小问题的解,即        f(3);

    结合所有情况,即f(4) = f(3) +f(2) * f(1) + f(1) * f(2) + f(3);

    为了规整化,我们定义f(0) = 1;于是f(4)可以重新写为:

    f(4) = f(0)*f(3) + f(1)*f(2) + f(2) * f(1)+ f(3)*f(0)

    然后我们推广到n,推广思路和n=4时完全一样,于是我们可以得到:

    f(n) = f(0)*f(n-1) + f(1)*f(n-2) + ... +f(n-1)*f(0)

    方法二:Catalan数:C(2n,n)/(n+1) (C(2n,n)表示2n里取n)或者C(2n,n)-C(2n,n-1)都可以解决。

    参考博客:http://blog.csdn.net/zyearn/article/details/7758716

    答案:35357670

    #include <iostream>
    #include <string.h>
    using namespace std;
    int main()
    {
        int f[20];
        memset(f,0,sizeof(f));
        f[0]=1;
        f[1]=1;
        f[2]=2;
        f[3]=5;
        for(int i=4; i<=16; i++)
        {
            for(int j=0; j<=i-1; j++)
                f[i]+=f[j]*f[i-1-j];
        }
        cout<<f[16]<<endl;
        return 0;
    }
    View Code

    3.信号匹配
     从X星球接收了一个数字信号序列。
     现有一个已知的样板序列。需要在信号序列中查找它首次出现的位置。这类似于串的匹配操作。
    如果信号序列较长,样板序列中重复数字较多,就应当注意比较的策略了。可以仿照串的KMP算法,进行无回溯的匹配。这种匹配方法的关键是构造next数组。
    next[i] 表示第i项比较失配时,样板序列向右滑动,需要重新比较的项的序号。如果为-1,表示母序列可以进入失配位置的下一个位置进行新的比较。

    下面的代码实现了这个功能,请仔细阅读源码,推断划线位置缺失的代码。

    s.kmp算法

    // 生成next数组 
    int* make_next(int pa[], int pn)
    {
        int* next = (int*)malloc(sizeof(int)*pn);
        next[0] = -1;
        int j = 0;
        int k = -1;
        while(j < pn-1){
            if(k==-1 || pa[j]==pa[k]){
                j++;
                k++;
                next[j] = k;
            }
            else
                k = next[k];
        }
        
        return next;
    }
    
    // da中搜索pa, da的长度为an, pa的长度为pn 
    int find(int da[], int an, int pa[], int pn)
    {
        int rst = -1;
        int* next = make_next(pa, pn);
        int i=0;  // da中的指针 
        int j=0;  // pa中的指针
        int n = 0;
        while(i<an){
            n++;
            if(da[i]==pa[j] || j==-1){
                i++;
                j++;
            }
            else
                j=next[j];//______________;//填空位置  
            
            if(j==pn) {
                rst = i-pn;
                break;
            }
        }
        
        free(next);
            
        return rst;
    }
    
    int main()
    {
        int da[] = {1,2,1,2,1,1,2,1,2,1,1,2,1,1,2,1,1,2,1,2,1,1,2,1,1,2,1,1,1,2,1,2,3};
        int pa[] = {1,2,1,1,2,1,1,1,2};
        
        int n = find(da, sizeof(da)/sizeof(int), pa, sizeof(pa)/sizeof(int));
        printf("%d
    ", n);
        
        return 0;
    }
    View Code

    4.生物芯片

    X博士正在研究一种生物芯片,其逻辑密集度、容量都远远高于普通的半导体芯片。
    博士在芯片中设计了 n 个微型光源,每个光源操作一次就会改变其状态,即:点亮转为关闭,或关闭转为点亮。
    这些光源的编号从 1 到 n,开始的时候所有光源都是关闭的。
    博士计划在芯片上执行如下动作:
    所有编号为2的倍数的光源操作一次,也就是把 2 4 6 8 ... 等序号光源打开
    所有编号为3的倍数的光源操作一次, 也就是对 3 6 9 ... 等序号光源操作,注意此时6号光源又关闭了。
    所有编号为4的倍数的光源操作一次。
     .....
    直到编号为 n 的倍数的光源操作一次。
    X博士想知道:经过这些操作后,某个区间中的哪些光源是点亮的。
    【输入格式】
    3个用空格分开的整数:N L R  (L<R<N<10^15)  N表示光源数,L表示区间的左边界,R表示区间的右边界。
    【输出格式】
    输出1个整数,表示经过所有操作后,[L,R] 区间中有多少个光源是点亮的。
    例如:
    输入:
    5 2 3
    程序应该输出:
    2
    再例如:
    输入:
    10 3 6
    程序应该输出:
    3
    资源约定:
    峰值内存消耗 < 256M
    CPU消耗  < 1000ms
    请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
    所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
    注意: main函数需要返回0
    注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
    注意: 所有依赖的函数必须明确地在源文件中 #include <xxx>, 不能通过工程设置而省略常用头文件。

    提交时,注意选择所期望的编译器类型。

    s.完全平方数的因子个数为奇数。

    一个光源最后是打开的,当且仅当他被操作了奇数次,也即是说该数的因子数为奇数,即该数为完全平方数,但是这题是不算1的,故最终答案是,若该数不是完全平方数,则最后为打开状态。

    #include<iostream>
    #include<stdio.h>
    #include<math.h>
    using namespace std;
    
    int main(){
        long long N,L,R;
        long long sum1,sum2;
        long long ans;
    
        scanf("%lld%lld%lld",&N,&L,&R);
    
        sum1=(int)(sqrt(L-1));
        sum2=(int)(sqrt(R));
    
        if(sum2>sum1){
            ans=R-L+1-(sum2-sum1);
        }
        else{
            ans=R-L+1;
        }
    
        printf("%lld
    ",ans);
    
        return 0;
    }
    View Code

    5.Log大侠

    atm参加了速算训练班,经过刻苦修炼,对以2为底的对数算得飞快,人称Log大侠。
    一天,Log大侠的好友 drd 有一些整数序列需要变换,Log大侠正好施展法力...
    变换的规则是: 对其某个子序列的每个整数变为: [log_2 (x) + 1]  其中 [] 表示向下取整,就是对每个数字求以2为底的对数,然后取下整。
    例如对序列 3 4 2 操作一次后,这个序列会变成 2 3 2。

    drd需要知道,每次这样操作后,序列的和是多少。

    【输入格式】

    第一行两个正整数 n m 。
    第二行 n 个数,表示整数序列,都是正数。
    接下来 m 行,每行两个数 L R 表示 atm 这次操作的是区间 [L, R],数列序号从1开始。
    【输出格式】
    输出 m 行,依次表示 atm 每做完一个操作后,整个序列的和。
    例如,输入:
    3 3
    5 6 4
    1 2
    2 3
    1 3
    程序应该输出:
    10
    8

    6

    【数据范围】

    对于 30% 的数据, n, m <= 10^3
    对于 100% 的数据, n, m <= 10^5
    资源约定:
    峰值内存消耗 < 256M
    CPU消耗  < 1000ms
    请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
    所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
    注意: main函数需要返回0
    注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
    注意: 所有依赖的函数必须明确地在源文件中 #include <xxx>, 不能通过工程设置而省略常用头文件。
    提交时,注意选择所期望的编译器类型。

    s.小数据可以暴力。大数据线段树。

    c.暴力代码,大数据会超时。

    #include <iostream>
    using namespace std;
    int a[100005];
    
    int log(int n)
    {
        int sum=1,ans=0;
        while(sum<n)
        {
            sum*=2;
            ans++;
        }
        if(sum==n)
            return ans+1;
        return ans;
    
    }
    
    int main()
    {
        int n,m,l,r,sum=0;
        cin>>n>>m;
        for(int i=1; i<=n; i++)
        {
            cin>>a[i];
            sum+=a[i];
        }
        for(int i=1; i<=m; i++)
        {
            cin>>l>>r;
            for(int j=l; j<=r; j++)
            {
                sum-=a[j];
                a[j]=log(a[j]);
                sum+=a[j];
            }
            cout<<sum<<endl;
        }
    
        return 0;
    }
    View Code

    c2.今天突然看了个题,那个是求开方的,恍然大悟。。。就是求几次对数后,不小于2的整数在均变为2。对于是1的数,进行下处理,见代码。

    这样判断区间内的数都是2之后,就不用向下更新了,降低了复杂度。

    #include"cstdio"
    #include"cmath"
    #include"algorithm"
    using namespace std;
    const int MAXN=100005;
    typedef long long LL;
    struct node{
        int l,r;
        LL sum;
    }segTree[MAXN*3];
    int cnt;
    void build(int rt,int l,int r)
    {
        segTree[rt].l=l;
        segTree[rt].r=r;
        if(l==r)
        {
            scanf("%lld",&segTree[rt].sum);
            if(segTree[rt].sum==1)
            {
                cnt++;//统计数值1的个数 ,方便优化程序 
                segTree[rt].sum++;//将所有1均变为2,防止1干扰程序优化 
            }
            return ;
        }
        int mid=(l+r)>>1;
        build(rt<<1,l,mid);
        build((rt<<1)|1,mid+1,r);
        segTree[rt].sum=segTree[rt<<1].sum+segTree[(rt<<1)|1].sum;
    }
    
    void update(int rt,int l,int r)
    {
        if(segTree[rt].sum==2*(segTree[rt].r-segTree[rt].l+1))    return ;//优化:不超过4轮,不小于2的整数在均变为2 
        
        if(segTree[rt].l==segTree[rt].r)
        {
            segTree[rt].sum=(LL)(log2(segTree[rt].sum*1.0)+1);
            return ;
        }
        
        int mid=(segTree[rt].l+segTree[rt].r)>>1;
        
        if(r<=mid)    update(rt<<1,l,r);
        else if(mid<l)    update((rt<<1)|1,l,r);
        else{
            update(rt<<1,l,mid);
            update((rt<<1)|1,mid+1,r);
        }
        segTree[rt].sum=segTree[rt<<1].sum+segTree[(rt<<1)|1].sum;
    }
    
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        build(1,1,n);
        while(m--)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            update(1,x,y);
            printf("%lld
    ",segTree[1].sum-cnt);
        }
    }
    View Code

    6.殖民地

    带着殖民扩张的野心,Pear和他的星际舰队登上X星球的某平原。为了评估这块土地的潜在价值,Pear把它划分成了M*N格,每个格子上用一个整数(可正可负)表示它的价值。
    Pear要做的事很简单——选择一些格子,占领这些土地,通过建立围栏把它们和其它土地隔开。对于M*N的格子,一共有(M+1)*N+M*(N+1)条围栏,即每个格子都有上下左右四个围栏;不在边界上的围栏被相邻的两个格子公用。大概如下图【p1.png】所示。
    图中,蓝色的一段是围栏,属于格子1和2;红色的一段是围栏,属于格子3和4。
    每个格子有一个可正可负的收益,而建围栏的代价则一定是正的。

    你需要选择一些格子,然后选择一些围栏把它们围起来,使得所有选择的格子和所有没被选的格子严格的被隔开。选择的格子可以不连通,也可以有“洞”,即一个连通块中间有一些格子没选。注意,若中间有“洞”,那么根据定义,“洞”和连通块也必须被隔开。

     Pear的目标很明确,花最小的代价,获得最大的收益。

    【输入数据】
    输入第一行两个正整数M N,表示行数和列数。
    接下来M行,每行N个整数,构成矩阵A,A[i,j]表示第i行第j列格子的价值。
    接下来M+1行,每行N个整数,构成矩阵B,B[i,j]表示第i行第j列上方的围栏建立代价。
    特别的,B[M+1,j]表示第M行第j列下方的围栏建立代价。
    接下来M行,每行N+1个整数,构成矩阵C,C[i,j]表示第i行第j列左方的围栏建立代价。
    特别的,C[i,N+1]表示第i行第N列右方的围栏建立代价。
    【输出数据】
    一行。只有一个正整数,表示最大收益。
    【输入样例1】
    3 3
    65 -6 -11
    15 65 32
    -8 5 66
    4 1 6
    7 3 11
    23 21 22
    5 25 22
    26 1 1 13
    16 3 3 4
    6 3 1 2
    程序应当输出:
    123
    【输入样例2】
    6 6
    72 2 -7 1 43 -12
    74 74 -14 35 5 3
    31 71 -12 70 38 66
    40 -6 8 52 3 78
    50 11 62 20 -6 61
    76 55 67 28 -19 68
    25 4 5 8 30 5
    9 20 29 20 6 18
    3 19 20 11 5 15
    10 3 19 23 6 24
    27 8 16 10 5 22
    28 14 1 5 1 24
    2 13 15 17 23 28
    24 11 27 16 12 13 27
    19 15 21 6 21 11 5
    2 3 1 11 10 20 9
    8 28 1 21 9 5 7
    16 20 26 2 22 5 12
    30 27 16 26 9 6 23
    程序应当输出
    870
    【数据范围】
    对于20%的数据,M,N<=4
    对于50%的数据,M,N<=15
    对于100%的数据,M,N<=200
    A、B、C数组(所有的涉及到的格子、围栏输入数据)绝对值均不超过1000。根据题意,A数组可正可负,B、C数组均为正整数。
    资源约定:
    峰值内存消耗 < 256M
    CPU消耗  < 3000ms
    请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
    所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
    注意: main函数需要返回0
    注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
    注意: 所有依赖的函数必须明确地在源文件中 #include <xxx>, 不能通过工程设置而省略常用头文件。

    提交时,注意选择所期望的编译器类型。

  • 相关阅读:
    Login
    2Sum,3Sum,4Sum,kSum,3Sum Closest系列
    Word Search II
    Word Search
    Linux命令四
    linux命令三
    Linux命令二
    Linux命令一
    网络基础
    操作系统
  • 原文地址:https://www.cnblogs.com/gongpixin/p/5518675.html
Copyright © 2020-2023  润新知