第七届蓝桥杯大赛个人赛省赛C++A组真题题解

题目链接

A组真题

题目结构

题目类型分值
第一题结果填空3分
第二题结果填空5分
第三题结果填空11分
第四题代码填空9分
第五题代码填空13分
第六题结果填空15分
第七题结果填空19分
第八题程序设计21分
第九题程序设计25分
第十题程序设计29分

第一题 网友年龄

  • 问题重现

    某君新认识一网友。
    当问及年龄时,他的网友说:
    “我的年龄是个2位数,我比儿子大27岁,
    如果把我的年龄的两位数字交换位置,刚好就是我儿子的年龄”

    请你计算:网友的年龄一共有多少种可能情况?

    提示:30岁就是其中一种可能哦.

    请填写表示可能情况的种数。
    注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

  • 解题思路

    枚举统计即可。由于其比自己儿子大27岁,故其至少27岁。

  • 代码

 /**
  * @filename:网友年龄.cbp
  * @Author : pursuit
  * @Blog:unique_pursuit
  * @email: 2825841950@qq.com
  * @Date : 2021-03-16-15.03.05
  */
  #include<bits/stdc++.h>
  
  using namespace std;
  
  typedef long long ll;
  const int INF = 0x3f3f3f3f;
  const int maxn=1e5+5;
  const int mod=1e9+7;
  
  //枚举统计即可。由于其比自己儿子大27岁,故其至少27岁。
  void solve(){
      int ans=0;
      for(int i=27;i<=99;i++){
          int temp1=i-27;
          int temp2=i/10*1+i%10*10;
          if(temp1==temp2){
              cout<<i<<endl;
              ans++;
          }
      }
      cout<<ans<<endl;
  }
  int main(){
      solve();
      return 0;
  }
  • 答案

    7 7 7


第二题 生日蜡烛

  • 问题重现

    某君从某年开始每年都举办一次生日party,并且每次都要吹熄与年龄相同根数的蜡烛。

    现在算起来,他一共吹熄了236根蜡烛。

    请问,他从多少岁开始过生日party的?

    请填写他开始过生日party的年龄数。
    注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

  • 解题思路

    由题意,我们可以枚举开始过生日party的年龄,然后统计在接下来的几年能不能吹熄这236根蜡烛。简单判断即可。

  • 代码

  /**
  * @filename:生日蜡烛.cbp
  * @Author : pursuit
  * @Blog:unique_pursuit
  * @email: 2825841950@qq.com
  * @Date : 2021-03-16-15.07.42
  */
  #include<bits/stdc++.h>
  
  using namespace std;
  
  typedef long long ll;
  const int INF = 0x3f3f3f3f;
  const int maxn=1e5+5;
  const int mod=1e9+7;
  
  //暴力枚举最开始过生日的年龄数,判断是否符合条件。
  //答案为26.
  void solve(){
      for(int i=1;i<236;i++){
          int temp=i,ans=i;
          while(ans<236){
              temp++;
              ans+=temp;
          }
          if(ans==236){
              cout<<i<<endl;
          }
      }
  }
  int main(){
      solve();
      return 0;
  }
  • 答案

    26 26 26


第三题 方格填数

  • 问题重现

    如下的10个格子

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2K8t5L6W-1615972064534)(第七届蓝桥杯大赛个人赛省赛C++A组真题题解.assets/image-20210317134146349.png)]

    填入0~9的数字。要求:连续的两个数字不能相邻。
    (左右、上下、对角都算相邻)

    一共有多少种可能的填数方案?

    请填写表示方案数目的整数。
    注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

  • 解题思路

    这题我们可以直接暴力全排列完成,当然也可以用 d f s dfs dfs,后者更高效一些,这里采用暴力全排列,都是同样的道理。解这道题之前我们先规定我们该怎么填:

在这里插入图片描述

我是这样填的,那么接下来就直接全排列根据条件判断统计即可。

  • 代码
  /**
  * @filename:方格填数.cbp
  * @Author : pursuit
  * @Blog:unique_pursuit
  * @email: 2825841950@qq.com
  * @Date : 2021-03-16-15.16.21
  */
  #include<bits/stdc++.h>
  
  using namespace std;
  
  typedef long long ll;
  const int INF = 0x3f3f3f3f;
  const int maxn=1e5+5;
  const int mod=1e9+7;
  
  //暴力全排列。答案为1580
  int a[]={0,1,2,3,4,5,6,7,8,9};
  void solve(){
      int ans=0;
      do{
          //由于我们的规定可以判断。若相邻,则其绝对值差不为1.
          if(  abs(a[1]-a[0])==1||abs(a[2]-a[1])==1||abs(a[3]-a[2])==1||abs(a[4]-a[3])==1||abs(a[5]-a[4])==1
             ||abs(a[6]-a[5])==1||abs(a[8]-a[7])==1||abs(a[9]-a[8])==1||abs(a[4]-a[1])==1||abs(a[5]-a[0])==1
             ||abs(a[7]-a[6])==1||abs(a[8]-a[5])==1||abs(a[9]-a[4])==1||abs(a[4]-a[2])==1||abs(a[3]-a[1])==1
             ||abs(a[4]-a[0])==1||abs(a[5]-a[1])==1||abs(a[6]-a[0])==1||abs(a[7]-a[5])==1||abs(a[6]-a[8])==1
             ||abs(a[8]-a[4])==1||abs(a[5]-a[9])==1||abs(a[9]-a[3])==1)continue;
          ans++;
      }while(next_permutation(a,a+10));
      cout<<ans<<endl;
  }
  int main(){
      solve();
      return 0;
  }
  • 答案

    1580 1580 1580


第四题 快速排序

  • 问题重现

    排序在各种场合经常被用到。
    快速排序是十分常用的高效率的算法。

    其思想是:先选一个“标尺”,
    用它把整个队列过一遍筛子,
    以保证:其左边的元素都不大于它,其右边的元素都不小于它。

    这样,排序问题就被分割为两个子区间。
    再分别对子区间排序就可以了。

    下面的代码是一种实现,请分析并填写划线部分缺少的代码。

    #include <stdio.h>
    
    void swap(int a[], int i, int j)
    {
    int t = a[i];
    a[i] = a[j];
    a[j] = t;
    }
    
    int partition(int a[], int p, int r)
    {
       int i = p;
       int j = r + 1;
       int x = a[p];
       while(1){
           while(i<r && a[++i]<x);
           while(a[--j]>x);
           if(i>=j) break;
           swap(a,i,j);
       }
       ________________________;
       return j;
    }
    
    void quicksort(int a[], int p, int r)
    {
       if(p<r){
           int q = partition(a,p,r);
           quicksort(a,p,q-1);
           quicksort(a,q+1,r);
       }
    }
       
    int main()
    {
    int i;
    int a[] = {5,13,6,24,2,8,19,27,6,12,1,17};
    int N = 12;
    
    quicksort(a, 0, N-1);
    
    for(i=0; i<N; i++) printf("%d ", a[i]);
    printf("\n");
    
    return 0;
    }
    

    注意:只填写缺少的内容,不要书写任何题面已有代码或说明性文字。

  • 解题思路

    会快排的应该一下子就能写出来,不会也没关系,我们注意到题目给出的思想,就是找一个标尺,让左边的小于它,右边的大于它。那么我们看这个partition函数,它就是先找一个标尺,然后从左开始找大于它的,从结尾逆着找小于它的,最后如果位置是小在左,大的在右的,那就不用交换。这个时候我们发现这个 j j j的坐标是大于标尺坐标的,所以为了达到让:==让左边的小于它,右边的大于它。==我们就必须交换 a [ j ] a[j] a[j] a [ p ] a[p] a[p],即swap(a,p,j)

  • 答案

    swap(a,p,j)


第五题 消除尾一

  • 问题重现

    下面的代码把一个整数的二进制表示的最右边的连续的1全部变成0
    如果最后一位是0,则原数字保持不变。

    如果采用代码中的测试数据,应该输出:
    00000000000000000000000001100111 00000000000000000000000001100000
    00000000000000000000000000001100 00000000000000000000000000001100

    请仔细阅读程序,填写划线部分缺少的代码。

    #include <stdio.h>
    
    void f(int x)
    {
    int i;
    for(i=0; i<32; i++) printf("%d", (x>>(31-i))&1);
    printf("   ");
    
    x = ___________;
    
    for(i=0; i<32; i++) printf("%d", (x>>(31-i))&1);
    printf("\n");	
    }
    
    int main()
    {
    f(103);
    f(12);
    return 0;
    }
    

    注意:只填写缺少的内容,不要书写任何题面已有代码或说明性文字。

  • 解题思路

    这道题确实不容易想到。我们来看,因为要消除连续的1,倘若有连续的1,我们加1,则相当于消除了连续的1.再做一下与运算,则可以取消进位1的影响。若没有连续的1,+1与自身做与运算结果还是本身。

  • 答案

    x&(x+1)


第六题 寒假作业

  • 问题重现

    现在小学的数学题目也不是那么好玩的。
    看看这个寒假作业:

    □ + □ = □
    □ - □ = □
    □ × □ = □
    □ ÷ □ = □

    每个方块代表1~13中的某一个数字,但不能重复。
    比如:
    6 + 7 = 13
    9 - 8 = 1
    3 * 4 = 12
    10 / 2 = 5

    以及:
    7 + 6 = 13
    9 - 8 = 1
    3 * 4 = 12
    10 / 2 = 5

    就算两种解法。(加法,乘法交换律后算不同的方案)

    你一共找到了多少种方案?

    请填写表示方案数目的整数。
    注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

  • 解题思路

    这道题暴力全排列显然是不行的,高达 A 13 13 A_{13}^{13} A1313。对于这种我们还有一种方法就是 d f s dfs dfs+剪枝优化了。我们先来规定我们的填数顺序:

在这里插入图片描述

我们按这个顺序dfs依次填数。剪枝即当一个式子填完之后就立马判断是否符合,这样剪枝能够大大的减小时间复杂度。

  • 代码
  /**
  * @filename:寒假作业.cbp
  * @Author : pursuit
  * @Blog:unique_pursuit
  * @email: 2825841950@qq.com
  * @Date : 2021-03-16-16.20.59
  */
  #include<bits/stdc++.h>
  
  using namespace std;
  
  typedef long long ll;
  const int INF = 0x3f3f3f3f;
  const int maxn=1e5+5;
  const int mod=1e9+7;
  
  //暴力枚举会爆栈,我们采用dfs搜索,注意剪枝。
  int a[12],ans=0;//ans统计方案总数。
  bool vis[14];//vis[i]表示数字i是否被使用过。
  void dfs(int step){
      //step表示我们现在所填的是第几个数
      if(step==3){
          //说明前三个数填完了,我们可以判断第一个式子。
          if(a[0]+a[1]!=a[2]){
              return;
          }
      }
      else if(step==6){
          //说明第二个式子填完了,我们可以判断。
          if(a[3]-a[4]!=a[5]){
              return;
          }
      }
      else if(step==9){
          //说明第三个式子填完了。
          if(a[6]*a[7]!=a[8]){
              return;
          }
      }
      else if(step==12){
          if(a[11]*a[10]==a[9]){
              ans++;
  //            for(int i=0;i<12;i++){
  //                cout<<a[i]<<" ";
  //            }
  //            cout<<endl;
          }
          return;
      }
      for(int i=1;i<=13;i++){
          if(!vis[i]){
              vis[i]=true;
              a[step]=i;
              dfs(step+1);
              vis[i]=false;
          }
      }
  }
  void solve(){
      dfs(0);
      cout<<ans<<endl;
  }
  int main(){
      solve();
      return 0;
  }
  • 答案

    64 64 64


第七题 剪邮票

  • 问题重现

    如[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p9dkIwci-1615972064545)(第七届蓝桥杯大赛个人赛省赛C++A组真题题解.assets/image-20210317143152402.png)]

    有12张连在一起的12生肖的邮票。
    现在你要从中剪下5张来,要求必须是连着的。
    (仅仅连接一个角不算相连)
    比如,[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qKG7bSXt-1615972064548)(第七届蓝桥杯大赛个人赛省赛C++A组真题题解.assets/image-20210317143245739.png)]

    中,粉红色所示部分就是合格的剪取。

    请你计算,一共有多少种不同的剪取方法。

    请填写表示方案数目的整数。
    注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

  • 解题思路

    对于这个题,我们可以分为两个步骤来看,一个就是从 12 12 12个数中选出 5 5 5个数,一个就是判断这五个数能否满足条件。这两个步骤其实都是搜索问题,我们先来看第一个步骤,就是简单的 d f s dfs dfs,注意状态标记与清除即可,关键在于第二个步骤,我们每次取的五个数都要进行判断,这个时候我们必须要设计几个数组a_flag以及graph_vis数组,其中第一个数组则是用来标记我们选择的这五个数,而第二个数组则是标记我们访问过的数。在方格里进行深搜的时候一定要注意判断边界以及统计我们访问过的数,跑完了之后若合格则统计。具体看代码。

  • 代码

 /**
  * @filename:剪邮票.cbp
  * @Author : pursuit
  * @Blog:unique_pursuit
  * @email: 2825841950@qq.com
  * @Date : 2021-03-16-16.50.18
  */
  #include<bits/stdc++.h>
  
  using namespace std;
  
  typedef long long ll;
  const int INF = 0x3f3f3f3f;
  const int maxn=1e5+5;
  const int mod=1e9+7;
  
  //答案为116
  int a[6];//存储我们选取的五个数,为了避免重复,我们采用dfs搜索。
  bool vis[13];//搜索时标记选过的数。
  bool a_flag[13];//判断时标记我们选过的数,作为辅助。
  bool graph_vis[3][4];//标记我们访问过的数。
  int go[][2]={{1,0},{-1,0},{0,1},{0,-1}};//遍历邻接点。
  int ans=0,cnt=0;//ans统计方案数,cnt统计我们可以剪的数。
  int graph[3][4];//存储二维图。
  void dfs_cut(int x,int y){
      int sx,sy;
      for(int i=0;i<4;i++){
          sx=x+go[i][0];
          sy=y+go[i][1];
          if(sx<0||sx>=3||sy<0||sy>=4||a_flag[graph[sx][sy]]==false||graph_vis[sx][sy])continue;
          graph_vis[sx][sy]=true;
          cnt++;
  //        cout<<"cnt的值:"<<cnt<<endl;
          dfs_cut(sx,sy);
      }
  }
  void check(){
      memset(a_flag,false,sizeof(a_flag));
      memset(graph_vis,false,sizeof(graph_vis));
      for(int i=1;i<=5;i++){
          a_flag[a[i]]=true;//标记。
  //        cout<<a[i]<<" ";
      }
  //    cout<<endl;
      bool flag=false;
      for(int i=0;i<3;i++){
          for(int j=0;j<4;j++){
              if(a_flag[graph[i][j]]){
                  //找到我们所选择的数。
                  cnt=1;
                  graph_vis[i][j]=true;//代表我们已经访问过。
                  dfs_cut(i,j);
                  flag=true;
                  break;
              }
          }
          if(flag)break;
      }
      if(cnt==5){
          ans++;
      }
  }
  void select_dfs(int step){
      //step表示我们当前选取的为第step个数。
      if(step==6){
          //说明前五个已经选完。我们可以开始判断是否连续。
          check();
          return;
      }
      //这里会重复选取,所以我们必须设置从小到大选取。
      for(int i=a[step-1]+1;i<=12;i++){
          if(!vis[i]){
              a[step]=i;
              vis[i]=true;
              select_dfs(step+1);
              vis[i]=false;
          }
      }
  }
  void solve(){
      select_dfs(1);
      cout<<ans<<endl;
  }
  int main(){
      //建立二维图。
      int temp=1;
      for(int i=0;i<3;i++){
          for(int j=0;j<4;j++){
              graph[i][j]=temp;
              temp++;
          }
      }
      solve();
      return 0;
  }
  • 答案

    116 116 116


第八题 四平方和

  • 问题重现

    四平方和定理,又称为拉格朗日定理:
    每个正整数都可以表示为至多4个正整数的平方和。
    如果把0包括进去,就正好可以表示为4个数的平方和。

    比如:
    5 = 0 2 + 0 2 + 1 2 + 2 2 5 = 0^2 + 0^2 + 1^2 + 2^2 5=02+02+12+22
    7 = 1 2 + 1 2 + 1 2 + 2 2 7 = 1^2 + 1^2 + 1^2 + 2^2 7=12+12+12+22

    对于一个给定的正整数,可能存在多种平方和的表示法。
    要求你对4个数排序:
    0 < = a < = b < = c < = d 0 <= a <= b <= c <= d 0<=a<=b<=c<=d
    并对所有的可能表示法按 a,b,c,d 为联合主键升序排列,最后输出第一个表示法

    程序输入为一个正整数N (N<5000000)
    要求输出4个非负整数,按从小到大排序,中间用空格分开

    例如,输入:
    5
    则程序应该输出:
    0 0 1 2

    再例如,输入:
    12
    则程序应该输出:
    0 2 2 2

    再例如,输入:
    773535
    则程序应该输出:
    1 1 267 838

    资源约定:
    峰值内存消耗 < 256M
    CPU消耗 < 3000ms

    请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

    所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

    注意: main函数需要返回0
    注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
    注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。

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

  • 解题思路

    我们应该很容易就能想到暴力枚举,通过枚举 a , b , c a,b,c a,b,c,利用 n n n确定 d d d,再判断即可。程序如下:

  void solve(){
      for(int a=0;a*a<=n;a++){
          for(int b=a;a*a+b*b<=n;b++){
              for(int c=b;a*a+b*b+c*c<=n;c++){
                  int d=sqrt(n-a*a-b*b-c*c);
                  //我们这即是按字典序升序遍历的,所以我们找到的第一个即是答案。
                  if(a*a+b*b+c*c+d*d==n){
                      cout<<a<<" "<<b<<" "<<c<<" "<<d<<endl;
                      return;
                  }
              }
          }
      }
  }

我们计算一下时间复杂度约为 O ( n × n × n ) O(\sqrt{n}\times\sqrt{n}\times\sqrt{n}) O(n ×n ×n ),而 n n n的范围是 5 e 6 5e6 5e6,这样算下来高达 5 e 10 5e10 5e10 3 s 3s 3s的时间限制大概也只能处理 1 e 9 1e9 1e9,这很难通过,当然,如果在数据不太强的情况下,是可以通过的,我在ACwing和其他OJ上都测试了一下,有的通过了有的没通过,那么我们能否再做优化呢?当然可以,我们默认为 a < = b < = c < = d a<=b<=c<=d a<=b<=c<=d,所以 a m a x × a m a x = n / 4 a_{max}\times a_{max}=n/4 amax×amax=n/4 b m a x × b m a x = ( n − a × a ) / 3 b_{max}\times b_{max}=(n-a\times a)/3 bmax×bmax=(na×a)/3,同理, c × c c\times c c×c的最大值就是 ( n − a × a − b × b ) / 2 (n-a\times a-b\times b)/2 (na×ab×b)/2,这样时间复杂度就可以被我们优化为: O ( n ) 与 O ( n 1.5 ) O(n)与 O(n^{1.5}) O(n)O(n1.5)之间,这样我们就可以通过。 当然,我们还有别的办法,对于网上提供的二分或者哈希表。它们的思路都是一样的,先找 c × c + d × d c\times c+d\times d c×c+d×d,将这个结果保存起来,再转而去计算 a × a + b × b a\times a+b\times b a×a+b×b,这样我们就可以在之前计算中的结果中寻找符合与 n − a × a − b × b n-a\times a-b\times b na×ab×b相等的值。我们来计算一下时间复杂度,二分法大约为 O ( n l o g n ) O(nlog_n) O(nlogn),而哈希表的插入和查找均为常数时间,那么其约为 O ( n ) O(n) O(n),这样看好像两种方法都能通过,且后者更高效,可事实真如此吗?当数据量足够大的时候,由于哈希表是无序的,虽然找的快,但在极端情况下就并不能处理。而二分则是最稳定的,此题哈希过不了,二分才可以。

  • 暴力枚举优化代码
  /**
  * @filename:四平方和.cbp
  * @Author : pursuit
  * @Blog:unique_pursuit
  * @email: 2825841950@qq.com
  * @Date : 2021-03-16-19.47.18
  */
  #include<bits/stdc++.h>
  
  using namespace std;
  
  typedef long long ll;
  const int INF = 0x3f3f3f3f;
  const int maxn=1e5+5;
  const int mod=1e9+7;
  
  int n;
  void solve(){
      for(int a=0;a*a*4<n;a++){
          for(int b=a;b*b*3<n-a*a;b++){
              for(int c=b;c*c*2<n-a*a-b*b;c++){
                  int d=(int)sqrt(n-a*a-b*b-c*c);
                  //我们这即是按字典序升序遍历的,所以我们找到的第一个即是答案。
                  if(a*a+b*b+c*c+d*d==n){
                      cout<<a<<" "<<b<<" "<<c<<" "<<d<<endl;
                      return;
                  }
              }
          }
      }
  }
  int main(){
      while(cin>>n){
          solve();
      }
      return 0;
  }
  • 二分法代码
  /**
  * @filename:四平方和定理.cbp
  * @Author : pursuit
  * @Blog:unique_pursuit
  * @email: 2825841950@qq.com
  * @Date : 2021-03-16-19.59.24
  */
  #include<bits/stdc++.h>
  
  using namespace std;
  
  typedef long long ll;
  const int INF = 0x3f3f3f3f;
  const int maxn=5e6+5;
  const int mod=1e9+7;
  
  //二分,核心思想先枚举c和d的平方和并存起来在枚举a和b.
  struct node{
      int sum,c,d;
      bool operator<(const node &a){
          if(sum==a.sum){
  
          }
          return sum<a.sum;
      }
  }s[maxn];
  int n,idx;
  void solve(){
      idx=0;
      for(int c=0;c*c<=n;c++){
          for(int d=c;c*c+d*d<=n;d++){
              s[idx++]={c*c+d*d,c,d};
          }
      }
      //存储好结果后,我们进行排序。
      sort(s,s+idx);
      for(int a=0;a*a<=n;a++){
          for(int b=a;a*a+b*b<=n;b++){
              int temp=n-a*a-b*b;
              int left=0,right=idx,mid;
              while(left<right){
                  mid=left+right>>1;
                  if(s[mid].sum<temp){
                      left=mid+1;
                  }
                  else{
                      right=mid;
                  }
                  if(s[mid].sum==temp){
                      cout<<a<<" "<<b<<" "<<s[mid].c<<" "<<s[mid].d<<endl;
                      return;
                  }
              }
          }
      }
  }
  int main(){
      while(cin>>n){
          solve();
      }
      return 0;
  }

第九题 密码脱落

  • 问题重现

    X星球的考古学家发现了一批古代留下来的密码。
    这些密码是由A、B、C、D 四种植物的种子串成的序列。
    仔细分析发现,这些密码串当初应该是前后对称的(也就是我们说的镜像串)。
    由于年代久远,其中许多种子脱落了,因而可能会失去镜像的特征。

    你的任务是:
    给定一个现在看到的密码串,计算一下从当初的状态,它要至少脱落多少个种子,才可能会变成现在的样子。

    输入一行,表示现在看到的密码串(长度不大于1000)
    要求输出一个正整数,表示至少脱落了多少个种子。

    例如,输入:
    ABCBA
    则程序应该输出:
    0

    再例如,输入:
    ABDCDCBABC
    则程序应该输出:
    3

    资源约定:
    峰值内存消耗 < 256M
    CPU消耗 < 1000ms

    请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

    所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

    注意: main函数需要返回0
    注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
    注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。

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

  • 解题思路

    一道简单的区间 d p dp dp,这道题实际上就是说变成最大回文子串需要减去的字符数量。即等价与总数量 − - 最大的回文串长度,所以我们需要去寻找最长的回文串。状态计算的选择方式和最长公共子序列类似。如下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cYWauJ27-1615972064550)(第七届蓝桥杯大赛个人赛省赛C++A组真题题解.assets/7416_6b12137a50-dbeb2ca84e7ee6b21319802cbd0170d.png)]

    这里我们只要考虑三个状态,最后一个状态是由第二第三个状态形成的。这里同时也给出区间 d p dp dp的解题模板。

  memset(dp,0,sizeof(dp));
  //初始dp数组
  for(int len=2;len<=n;len++){
      //枚举区间长度
      for(int i=1;i+len-1<n;++i){//枚举区间的起点
          int j=i+len-1;//根据起点和长度得出终点
          for(int k=i;k<=j;++k)//枚举最优分割点
              //状态转移方程
          }
  }
  • 代码
  /**
  * @filename:密码脱落.cbp
  * @Author : pursuit
  * @Blog:unique_pursuit
  * @email: 2825841950@qq.com
  * @Date : 2021-03-16-20.38.45
  */
  #include<bits/stdc++.h>
  
  using namespace std;
  
  typedef long long ll;
  const int INF = 0x3f3f3f3f;
  const int maxn=1e3+5;
  const int mod=1e9+7;
  
  int f[maxn][maxn];//f[l][r]表示[l,r]区间的回文子序列长度。
  string s;
  int n;//字符串长度。
  void solve(){
      n=s.size();
      for(int len=1;len<=n;len++){
          //len表示我们分割的子串长度。
          for(int i=0;i+len-1<n;i++){
              //i表示我们分割的子串起点。
              int j=i+len-1;//子串终点。
              if(len==1){
                  //此时易知都为1.
                  f[i][j]=1;
              }
              else{
                  f[i][j]=max(f[i+1][j],f[i][j-1]);
                  if(s[i]==s[j]){
                      f[i][j]=max(f[i][j],f[i+1][j-1]+2);
                  }
              }
          }
      }
      cout<<n-f[0][n-1]<<endl;
  }
  int main(){
      while(cin>>s){
          solve();
      }
      return 0;
  }

第十题 最大比例

  • 问题重现

    X星球的某个大奖赛设了M级奖励。每个级别的奖金是一个正整数。
    并且,相邻的两个级别间的比例是个固定值。
    也就是说:所有级别的奖金数构成了一个等比数列。比如:
    16,24,36,54
    其等比值为:3/2

    现在,我们随机调查了一些获奖者的奖金数。
    请你据此推算可能的最大的等比值。

    输入格式:
    第一行为数字 N (0<N<100),表示接下的一行包含N个正整数
    第二行N个正整数Xi(Xi<1 000 000 000 000),用空格分开。每个整数表示调查到的某人的奖金数额

    要求输出:
    一个形如A/B的分数,要求A、B互质。表示可能的最大比例系数

    测试数据保证了输入格式正确,并且最大比例是存在的。

    例如,输入:
    3
    1250 200 32

    程序应该输出:
    25/4

    再例如,输入:
    4
    3125 32 32 200

    程序应该输出:
    5/2

    再例如,输入:
    3
    549755813888 524288 2

    程序应该输出:
    4/1

    资源约定:
    峰值内存消耗 < 256M
    CPU消耗 < 3000ms

  • 解题思路

    我们可以假设这原数列就为: a , a ∗ ( p / q ) 1 , a ∗ ( p / q ) 2 , . . . , a ∗ ( p / q ) ( n − 1 ) a,a*(p/q)^1,a*(p/q)^2,...,a*(p/q)^{(n-1)} a,a(p/q)1,a(p/q)2,...,a(p/q)(n1)其中公比为 p / q p/q p/q。那么我们再假设一下,对于抽取的数列: b 0 , b 1 , . . . , b ( N − 1 ) b_0,b_1,...,b_{(N-1)} b0,b1,...,b(N1)(我们已经对其从小到大排序),那么根据等比数列的性质,有: b 1 / b 0 , b 2 / b 0 , . . . , b ( N − 1 ) / b 0 − − > ( p / q ) x 1 , ( p / q ) x 2 , . . . , ( p / q ) x ( N − 1 ) b_1/b_0,b_2/b_0,...,b_{(N-1)}/b_0--> (p/q)^{x_1},(p/q)^{x_2},...,(p/q)^{x_{(N-1)}} b1/b0,b2/b0,...,b(N1)/b0>(p/q)x1,(p/q)x2,...,(p/q)x(N1)。注意上面的公比和这里的 p / q p/q p/q是不一样的,上面的为 k k k,这里的可不为 k k k,我们要求的最大值其实就是它们指数的最大公约数。这样一看,我们思路就清晰了,先排序再利用 g c d gcd gcd得到 p x , q x p^x,q^x px,qx,然后我们就是要求取指数的最大公约数和底数的最大公约数了,可是这里最关键的一点就是我们只知道 ( p / q ) x (p/q)^x (p/q)x,这可怎么办呢?这里引入更相减损术,也是求最大公约数的办法,这里不细说不证明,只给出步骤:

    1. 任意给定两个正整数;判断它们是否都是偶数。若是,则用2约简;若不是则执行第二步。
    2. 以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。

    最后所得的减数即是最大公约数。那么由于这里利用是减法,我们就是要利用指数上的减法来求最大公约数,而我们知道 q a / q b = q a − b q^a/q^b=q^{a-b} qa/qb=qab,所以我们作除法即是在对指数上作减法,最后求得相等即是说明差为0,也就是相等,那么那个数即是最大公约数。仔细理解。

  • 代码

 /**
  * @filename:最大比例.cbp
  * @Author : pursuit
  * @Blog:unique_pursuit
  * @email: 2825841950@qq.com
  * @Date : 2021-03-16-21.40.49
  */
  #include<bits/stdc++.h>
  
  using namespace std;
  
  typedef long long ll;
  const int INF = 0x3f3f3f3f;
  const int maxn=110;
  const int mod=1e9+7;
  
  //辗转相除法求最大公约数。
  ll gcd(ll n,ll m){
      ll temp;
      while(n%m){
          temp=n%m;
          n=m;
          m=temp;
      }
      return m;
  }
  ll gcd_sub(ll n,ll m){
      //由于这道题我们是要计算指数的最大公约数,所以相减的话改为除,相等的时候即为1.
      if(n<m)swap(n,m);
      if(m==1)return n;
      return gcd_sub(m,n/m);
  }
  ll X[maxn],A[maxn],B[maxn];
  int n;
  void solve(){
      sort(X,X+n);
      int cnt=0;
      for(int i=1;i<n;i++){
          if(X[i]!=X[i-1]){
              ll temp=gcd(X[i],X[0]);
              A[cnt]=X[i]/temp;//得到X[i]/X[0]的分子
              B[cnt]=X[0]/temp;//得到X[i]/x[0]的分母。
              cnt++;
          }
      }
      //接下来求取最大公约数。
      ll up=A[0],down=B[0];
      for(int i=1;i<cnt;i++){
          up=gcd_sub(up,A[i]);
          down=gcd_sub(down,B[i]);
      }
      ll temp=gcd(up,down);
      cout<<up/temp<<"/"<<down/temp<<endl;
  }
  int main(){
      while(cin>>n){
          for(int i=0;i<n;i++){
              cin>>X[i];
          }
          solve();
      }
      return 0;
  }
posted @ 2022-03-26 16:49  unique_pursuit  阅读(92)  评论(0)    收藏  举报