第十二届蓝桥杯C++ B组题解(A-H)

目录

试题A:空间

试题B:卡片

试题C:直线

试题D:货物摆放 

试题E:路径 

试题F:时间显示

试题G:砝码称重

试题H:杨辉三角形


试题A:空间

#include <iostream>
using namespace std;
int main()
{
  //  1MB=1024KB   1KB=1024字节(byte)  1byte=8位
  cout<<(long long)256*1024*1024*8/32;
  return 0;
}

答案是:  67108864

试题B:卡片

思路:模拟

具体看代码注解(非常详细)

答案是: 3181

#include<bits/stdc++.h>
using namespace std;

int s[10]; // 存储0~9每一个数字的个数
bool check(int x){
    // 枚举x的每一位数字
    while(x){
        int t=x%10; // 取出x的各位数字
        x/=10; // 去掉个位数字,比如 201/10=20
        // 先将t这个数字的个数减一,然后判断是否还有t这个数字,如果没有则返回false
        if(--s[t]<0) return false;
    }
    return true; 
}
int main(){
    for(int i=0;i<10;i++) s[i]=2021; // 初始时每一个数字都有2021个
    // 从前往后依次枚举每一个数
    for(int i=1;;i++){
        // 如果第i个数不能拼出来,那么拼出来的数应该是i-1
        if(!check(i)){
            cout<<i-1; // 3181
            return 0;
        }
    }
    return 0;
}

试题C:直线

思路:模拟

定义一个结构体存储每条直线的斜率和截距,枚举所有点对,如果斜率存在(x1!=x2),则求出当前的斜率k,和截距b,加入到结构体数组中,然后将数组按斜率截距排序,如果相邻的两个直线的斜率不同或者截距不同,则答案加一

答案是: 40257

#include<bits/stdc++.h>
using namespace std;
const int N=2e5; // 最多的线数
int n;
struct Line{
    double k,b; // 斜率和截距
    bool operator<(const Line&t)const{
        if(k!=t.k) return k<t.k;
        return b<t.b;
    }
}l[N];
int main(){
    // 枚举所有点对
    for(int x1=0;x1<20;x1++){
        for(int y1=0;y1<21;y1++){
            for(int x2=0;x2<20;x2++){
                for(int y2=0;y2<21;y2++){
                    // 不枚举斜率不存在的点对,即x1=x2,最后加上20即可
                    if(x1!=x2){
                        double k=(double)(y2-y1)/(x2-x1);
                        double b=y1-k*x1;
                        l[n++]={k,b};
                    }
                }
            }
        }
    }
    sort(l,l+n);
    int res=1;
    for(int i=1;i<n;i++){
        // 如果相邻两个点对的斜率或者截距不同,则答案++
        // 注意c++浮点数有误差判断浮点数相等时要注意
        if(fabs(l[i].k-l[i-1].k)>1e-8||fabs(l[i].b-l[i-1].b)>1e-8){
            res++;
        }
    }
    cout<<res+20<<endl; // 40257
    return 0;
}

试题D:货物摆放 

思路:模拟

求出n的所有约数,然后暴力枚举a,b,c即可

答案是: 2430

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
    ll n=2021041820210418;
    vector<ll> d; // 保存n的所有约数
    for(ll i=1;i*i<=n;i++){
        if(n%i==0){
            d.push_back(i);
            // 8/2=4!=2 说明4也是8的约数
            if(n/i!=i) d.push_back(n/i);
        }
    }
    int res=0;
    // 枚举a,b,c
    for(auto a:d)
        for(auto b:d)
            for(auto c:d)
                if(a*b*c==n)
                    res++;
    cout<<res<<endl; // 2430
    return 0;
}

试题E:路径 

思路:最短路

最短路算法都可以,这里我用的是spfa

答案是:10266837

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2200,M=N*50;
int n;
int h[N],e[M],w[M],ne[M],idx;
int q[N],dist[N];
bool st[N];
int gcd(int a,int b){
    return b?gcd(b,a%b):a;
}
void add(int a,int b,int c){
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void spfa(){
    queue<int> q;
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    q.push(1);
    st[1]=true;
    while(q.size()){
        int t=q.front();q.pop();
        st[t]=false;
        for(int i=h[t];i!=-1;i=ne[i]){
            int j=e[i];
            if(dist[j]>dist[t]+w[i]){
                dist[j]=dist[t]+w[i];
                if(!st[j]){
                    q.push(j);
                    st[j]=true;
                }
            }
        }
    }
}
int main(){
    n=2021;
    memset(h,-1,sizeof h);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(fabs(i-j)<=21){
                int d=gcd(i,j);
                add(i,j,i*j/d); // 最小公倍数  i*j/d
            }
        }
    }
    spfa();
    cout<<dist[n]<<endl; // 10266837
    return 0;
}

试题F:时间显示

小蓝要和朋友合作开发一个时间显示的网站。

在服务器上,朋友已经获取了当前的时间,用一个整数表示,值为从 1970 年 1 月 1 日 00:00:00 到当前时刻经过的毫秒数。

现在,小蓝要在客户端显示出这个时间。

小蓝不用显示出年月日,只需要显示出时分秒即可,毫秒也不用显示,直接舍去即可。

给定一个用整数表示的时间,请将这个时间对应的时分秒输出。

输入格式

输入一行包含一个整数,表示时间。

输出格式

输出时分秒表示的当前时间,格式形如 HH:MM:SS,其中 HH 表示时,值为 0 到 23,M 表示分,值为 0 到 59,S 表示秒,值为 0 到 59。

时、分、秒不足两位时补前导 0。

数据范围

对于所有评测用例,给定的时间为不超过 10e18 的正整数。

输入样例1:

46800999

输出样例1:

13:00:00

输入样例2:

1618708103123

输出样例2:

01:08:23

思路:数学计算(c++)  日期类(Java)

C++写法: 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
    ll n;
    cin>>n;
    n/=1000; // 去掉毫秒数
    n%=(24*60*60); // 去掉整的天数,保留时分秒
    int h=n/3600;
    n%=3600; // 去掉小时
    int m=n/60;
    n%=60; // 去掉分钟
    int s=n;
    printf("%02d:%02d:%02d",h,m,s); // 02表示补零
    return 0;
}

Java写法:

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		long n = sc.nextLong();
		Date date = new Date(n);
		SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
		System.out.println(sdf.format(date));
		sc.close();
	}
}

试题G:砝码称重

你有一架天平和 N 个砝码,这 N 个砝码重量依次是 W1,W2,⋅⋅⋅,WN。

请你计算一共可以称出多少种不同的正整数重量?

注意砝码可以放在天平两边。

输入格式

输入的第一行包含一个整数 N。

第二行包含 N 个整数:W1,W2,W3,⋅⋅⋅,WN。

输出格式

输出一个整数代表答案。

数据范围

对于 50% 的评测用例,1≤N≤15。
对于所有评测用例,1≤N≤100,N 个砝码总重不超过 105。

输入样例:

3
1 4 6

输出样例:

10

样例解释

能称出的 10 种重量是:1、2、3、4、5、6、7、9、10、11。

1 = 1;
2 = 6 − 4 (天平一边放 6,另一边放 4);
3 = 4 − 1;
4 = 4;
5 = 6 − 1;
6 = 6;
7 = 1 + 6;
9 = 4 + 6 − 1;
10 = 4 + 6;
11 = 1 + 4 + 6。

思路:背包(有限制的选择问题)O(n*m)

f[i][j]:表示只从前i个砝码中选,且总重量为j的所有方案的集合

求状态转义方程有一个简单的思想,就是 “看最后一步由什么推出来”

这里f[i][j]可以由前一步不选砝码,选砝码放在右边,选砝码放在左边这三种情况

1.不选砝码:f[i-1][j]

2.选砝码放在左边:f[i-1][j-w[i]] 选完w[i]之后 变成f[i][j] 这里w[i]为正

3.选砝码放在右边:f[i-1][j+w[i]] 选完w[i]之后 变成f[i][j] 这里w[i]为负

最后看一下f[n][1]~f[n][m] 有多少个非空即可

因为c++数组下标不能为负,所有要把所有的j加上一个偏移量m,这样j的范围为0~2m

C++写法:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=110,M=200010,B=M/2;//B是偏移量
int n,m;
int w[N];
bool f[N][M];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&w[i]),m+=w[i];
    f[0][B]=true;
    for(int i=1;i<=n;i++){
        for(int j=-m;j<=m;j++){
            // 有三种情况,如果三种情况中有一种情况为true,则
            // f[i][j]为true 所有需要 | 运算 保证有一个为true即可
            f[i][j+B]=f[i-1][j+B]; //不选砝码
            if(j-w[i]>=-m) f[i][j+B]|=f[i-1][j-w[i]+B];//选砝码放在左边
            if(j+w[i]<=m) f[i][j+B]|=f[i-1][j+w[i]+B];//选砝码放在右边
        }
    }
    int res=0;
    for(int j=1;j<=m;j++){
        if(f[n][j+B]) res++;
    }
    cout<<res<<endl;
    return 0;
}

Java写法: 略

试题H:杨辉三角形

下面的图形是著名的杨辉三角形:

如果我们按从上到下、从左到右的顺序把所有数排成一列,可以得到如下数列:

1, 1, 1, 1, 2, 1, 1, 3, 3, 1, 1, 4, 6, 4, 1, ...

给定一个正整数 N,请你输出数列中第一次出现 N 是在第几个数?

输入格式

输入一个整数 N。

输出格式

输出一个整数代表答案。

数据范围

对于 20% 的评测用例,1≤N≤10;
对于所有评测用例,1≤N≤10e9。

输入样例:

6

输出样例:

13

思路:二分 O(16logn)  找规律

显然,杨辉三角是对称的,每一个斜行都递增

同一行越靠下越大,所有要从最下方的斜行开始找,从内往外枚举

从第16个斜行开始枚举,如果第16个斜行中存在,则返回

否则看第15个斜行,每一个斜行都是单调递增的

因此可以二分一下,看一下每一个斜行是否存在目标值

C++写法: 

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int n;
LL C(int a, int b) {
    LL res = 1;
    for (int i = a, j = 1; j <= b; i --, j ++ ) {
        res = res * i / j;
        if (res > n) return res;
    }
    return res;
}
bool check(int k) {
    LL l = k * 2, r = max((LL)n, l);
    while (l < r) {
        LL mid = l + r >> 1;
        if (C(mid, k) >= n) r = mid;
        else l = mid + 1;
    }
    if (C(r, k) != n) return false;
    cout << r * (r + 1) / 2 + k + 1 << endl;
    return true;
}
int main() {
    cin >> n;
    for (int k = 16; ; k -- )
        if (check(k))
            break;
    return 0;
}

Java写法: 略

后面两题太难了。。。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

练习时长一年半的Java实习生

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

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

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

打赏作者

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

抵扣说明:

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

余额充值