第九届蓝桥杯国赛C++A组真题题解

题目结构

题目类型分值
第一题结果填空13分
第二题结果填空39分
第三题代码填空27分
第四题程序设计41分
第五题程序设计71分
第六题程序设计109分

第一题 三角形面积

  • 题面

    已知三角形三个顶点在直角坐标系下的坐标分别为:
    (2.3, 2.5)
    (6.4, 3.1)
    (5.1, 7.2)
    求该三角形的面积。

    输出

    要求精确到小数后3位,如不足3位,需要补零。

  • 解题思路

    两种做法,一种是海伦公式: S = s q r t ( p × ( p − a ) × ( p − b ) × ( p − c ) ) S=sqrt(p\times (p-a)\times (p-b)\times (p-c)) S=sqrt(p×(pa)×(pb)×(pc)),其中 p = a + b + c 2 p=\frac{a+b+c}{2} p=2a+b+c;还有一种是叉积公式: S = 1 2 × ∣ x 1 y 2 − x 2 y 1 ∣ S=\frac{1}{2}\times |x_1y_2-x_2y_1| S=21×x1y2x2y1

  • 代码

/**
  *@filename:三角形面积
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-05-07 21:39
**/
#include <bits/stdc++.h>
#define x first
#define y second

using namespace std;

typedef pair<double,double> pdd;
typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;

pdd points[3];
double dist(pdd a,pdd b){
    return sqrt((b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y)); 
}
void solve(){
    //已知三条边计算三角形的面积,可以利用海伦公式,也可以使用叉积公式。
    //海伦公式:S=sqrt(p*(p-a)*(p-b)*(p-c)),其中p=(a+b+c)/2
    double a=dist(points[0],points[1]),b=dist(points[1],points[2]),c=dist(points[0],points[2]);
    double p=(a+b+c)/2;
    printf("%.03f\n",sqrt(p*(p-a)*(p-b)*(p-c)));
    //叉积公式
    printf("%.03f\n",0.5*abs((points[1].x-points[0].x)*(points[2].y-points[0].y)-(points[1].y-points[0].y)*(points[2].x-points[0].x)));
    //8.795

}
int main(){
    for(int i=0;i<3;i++){
        cin>>points[i].x>>points[i].y;
    }
    solve();
    return 0;
}
  • 答案

    8.795 8.795 8.795

第二题 阅兵方阵

  • 题面

    x国要参加同盟阅兵活动。
    主办方要求每个加盟国派出的士兵恰好能组成 2 个方阵。
    x国发现弱小的 y国派出了130人的队伍,他们的士兵在行进中可以变换2种队形:
    130 = 81 + 49 = 9^2 + 7^2
    130 = 121 + 9 = 11^2 + 3^2
    x国君很受刺激,觉得x国面积是y国的6倍,理应变出更多队形。
    于是他发号施令:
    我们要派出一支队伍,在行进中要变出 12 种队形!!!
    手下人可惨了,要忙着计算至少多少人才能组成 12 种不同的双方阵。
    请你利用计算机的优势来计算一下,至少需要多少士兵。

    输出

    输出一个整数表示答案

  • 解题思路

    暴力枚举即可。

  • 代码

/**
  *@filename:阅兵方阵
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-05-07 21:56
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;

//要求士兵能恰好组成两个方阵。不一样恰好是两种队形,暴力枚举即可。对于出现过的标记即可。
int a[maxn];
void init(){
    for(int i=1;i<1000;i++){
        a[i]=i*i;
    }
}
int check(int x){
    int ans=0;
    for(int i=1;a[i]*2<=x;i++){
        int temp=sqrt(x-a[i]);
        if(temp*temp==x-a[i]){
            ans++;
        }
    }
    return ans;
}
void solve(){
    init();
    for(int i=1;;i++){
        if(check(i)==12){
            cout<<i<<endl;//160225
            break;
        }
    }
}
int main(){
    solve();
    return 0;
}
  • 答案

    160225 160225 160225

第三题 找假币

  • 题面

    在8枚硬币中,有1枚假币,假币外观与真币一模一样,只是重量略轻或略重一点。 给你一架天平,要求最多称3次,就找出假币,并且知道它是重一些还是轻一些。 下面的代码给出一个解决方案,仔细分析逻辑,填写划线位置缺少的代码。

    #include<bits/stdc++.h>
    
    //判断天平两边哪边重。
    int balance(int a, int b)
    {
        if(a<b) return -1;
        if(a>b) return 1;
        return 0;
    }
    
    void judge(char* data, int a, int b, int std)
    {
        //judge判断并输出相关信息。
        switch(balance(data[a],data[std])){
            case -1:printf("%d light\n", a);break;
            case 0:printf("%d heavy\n", b);break;
            case 1:printf("err!\n", b);break;
        }
    }
    
    // data 中8个元素,有一个假币,或轻或重
    void f(char* data)
    {
        //易知这里也是一个比较的地方。
        //switch( ____________________________________ ){ // 填空
        switch(balance(data[0]+data[1]+data[2],data[3]+data[4]+data[5])){ // 填空
            case -1:
                //如果为-1,说明左边小于右边。那么我们需要交叉组合判断。
                switch(balance(data[0]+data[4],data[3]+data[1])){
                    case -1:judge(data,0,3,1);break;//如果左边小于右边。
                    case 0:judge(data,2,5,0);break;//如果相等,说明假币只可能在2和5之中。判断即可。
                    case 1:judge(data,1,4,0);
                }
            break;
            case 0:judge(data,6,7,0);break;//相等,说明只可能在6和7中出现。0是比较值,说明之前的实际上在判断除了6,7之外的。
            case 1:
                switch(balance(data[0]+data[4],data[3]+data[1])){
                    case -1:judge(data,4,1,0);break;
                    case 0:judge(data,5,2,0);break;
                    case 1:judge(data,3,0,1);
                }
            break;
        }
    }
    
    int main()
    {
        int n;
        char buf[100];
        scanf("%d", &n);
        gets(buf);//gets被删除了。
        int i;
        for(i=0; i<n; i++){
            gets(buf);
            f(buf);
        }
        return 0;
    }
    
  • 解题思路

    上述代码已贴详细分析思路。

  • 答案

    balance(data[0]+data[1]+data[2],data[3]+data[4]+data[5])

第四题 约瑟夫环

  • 题面

    n 个人的编号是 1~n,如果他们依编号按顺时针排成一个圆圈,从编号是1的人开始顺时针报数。
    报数是从1报起,当报到 k 的时候,这个人就退出游戏圈。下一个人重新从1开始报数。
    求最后剩下的人的编号。这就是著名的约瑟夫环问题。
    本题目就是已知 n,k 的情况下,求最后剩下的人的编号。

    输入

    输入两个整数n,k(0<n,k<10^6)

    输出

    要求输出一个整数,表示最后剩下的人的编号。

    样例输入

    10 3
    

    样例输出

    4
    
  • 解题思路

    这题应该是递推模板题了吧。为了方便处理,我们假设编号从 0 0 0开始,那我们来简单分析一下,对于 n = 1 n=1 n=1的时候,那么自然存活的就是 0 0 0,而如果 n = 2 n=2 n=2,那么就会先排掉轮了第 k k k个人的时候,也就是 k % i k\%i k%i,那么最后剩下的那个实际上就是 ( f [ n − 1 ] + k ) % n (f[n-1]+k)\%n (f[n1]+k)%n,因为编号变了。同理这样一直往后,得到递推式 f [ n ] = ( f [ n − 1 ] + k ) % n f[n]=(f[n-1]+k)\%n f[n]=(f[n1]+k)%n。故此题易解。

  • 代码

/**
  *@filename:约瑟夫环
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-05-08 16:33
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1000000 + 5;
const int mod = 1e9+7;

int n,k;
int f[maxn];
void solve(){
    f[1]=0;
    for(int i=2;i<=n;i++){
        f[i]=(f[i-1]+k)%i;
    }
    cout<<f[n]+1<<endl;
}
int main(){
    cin>>n>>k;
    solve();
    return 0;
}

第五题 自描述序列

  • 题面

    小明在研究一个序列,叫Golomb自描述序列,不妨将其记作{G(n)}。这个序列有2个很有趣的性质:

    \1. 对于任意正整数n,n在整个序列中恰好出现G(n)次。
    \2. 这个序列是不下降的。
    以下是{G(n)}的前几项:
    n 1 2 3 4 5 6 7 8 9 10 11 12 13
    G(n) 1 2 2 3 3 4 4 4 5 5 5 6 6
    给定一个整数n,你能帮小明算出G(n)的值吗?

    输入

    输入存在多组数据,对于每组测试数据,输入一行包含一个整数n
    (1<=n<=2000000000000000)

    输出

    对于每组测试数据,输出一个整数表示答案。

    样例输入

    13
    

    样例输出

    6
    
  • 解题思路

    首先我们需要知道在 G G G中有 g [ i ] g[i] g[i] i i i,所以我们可以利用这个先求出 1 e 6 1e6 1e6 g [ i ] g[i] g[i],这样就可以推出 1 e 6 1e6 1e6的所有要迭代的次数。我们利用跟求前缀的思想一样不断迭代到 n n n。这样就可以求出 G ( n ) G(n) G(n)

  • 代码

/**
  *@filename:自描述序列
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-05-09 12:27
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1000000 + 5;
const int mod = 1e9+7;

ll n,g[maxn];
//在g数组中有g[i]个i。利用这个求前缀和就可以达到目的。
void init(){
    g[1]=1,g[2]=2;
    for(int i=2,j=2;i<maxn;i++){
        for(int k=0;k<g[i]&&j<maxn;k++)g[j++]=i;
    }
}
void solve(){
    ll s,t;
    s=t=0;
    for(ll i=1;;i++){
        s+=i*g[i];//过渡完直接跳到下一个。
        if(s>=n){
            //说明当前已经过了n。
            s-=i*g[i];//先减掉当前加过头的。
            cout<<t+(n-s+i-1)/i<<endl;//t为当前开头的值。
            break;
        }
        t+=g[i];//t获取的就是每次出现的值。
    }
}
int main(){
    init();
    while(cin>>n)
        solve();
    return 0;
}

第六题 采油

  • 题面

    LQ公司是世界著名的石油公司,为世界供应优质石油。
    最近,LQ公司又在森林里发现了一大片区域的油田,可以在这个油田中开采n个油井
    LQ公司在这n个油井之间修建了n-1条道路,每条道路连接两个油井,路径中间不会路过任何油井,而且这些道路将所有油井连通。
    建立油井的时候需要使用一台大型设备,运输起来非常麻烦。LQ公司准备在其中的一个油井位置建立一个空运站。
    先将设备空运到空运站,之后每次经过他们建立的道路来运输这个大型设备以建立不同的油井,当油井建立完毕后再从空运站将大型设备运走。
    为了减少运输的麻烦,公司要求大型设备在道路上运输的总路程是最短的。
    在建立油井和采油的过程中需要花费一些人力,第i个油井需要花费Bi个人,而一旦油井建成,就需要Si个人一直坚守在油井上进行维护。
    当然,如果一个人参与了油井的建设,他可以直接留下来维护油井,或者参与下一个油井的建设,但是在维护油井的人不能再参加后续油井的建设了。
    现在LQ公司想知道,大型设备运输的总路径长度最短是多少?
    在保证总路径长度最短的情况下,LQ公司至少需要花费多少人力才能完成所有油井的建立与维护。

    输入

    第一行包含一个整数n,表示油井的数量。油井由1到n依次标号。
    第二行包含n个整数,依次表示B1, B2, …, Bn,相邻的整数之间用一个空格分隔。
    第三行包含n个整数,依次表示S1, S2, …, Sn,相邻的整数之间用一个空格分隔。
    接下来n-1行描述油井之间的道路,其中的第i行包含两个整数a,b,用一个空格分隔。
    表示一条道路的起点为i+1、终点为a,长度为b,道路是双向的,设备可以从任意一端运送到另一端,每条道路都可以经过任意多次。
    数据保证任意两个油井之间都可以通过道路连接。

    n不超过100000,B、S、c均为不超过10000的正整数。

    输出

    输出包含两个整数,用一个空格分隔。
    表示最优情况下大型设备需要运输的总路程,以及在总路程最短的情况下最少需要花费的人力数量。

    样例输入

    2
    10 20
    15 15
    1 8
    

    样例输出

    16 30
    

    提示

    有两种方案达到最优。
    方案一:在油井2建立空运站,先建立油井2,再将大型设备运输到油井1建立油井1,最后将大型设备运回油井2。
    方案二:在油井1建立空运站,先将大型设备运输到油井2建立油井2,再将大型设备运送到油井1建立油井1。

  • 解题思路

    暂时不会,待补。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HeZephyr

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值