2020蓝桥杯C/C++ B组 第二场

试题A :门牌制作

  • 题意:计算1——2020有多少个2出现
  • 直接暴力跑一边统计就好
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 7;
const int mod = 1000000009;
typedef long long ll;
int cnt(int x) {
    int re = 0;
    while(x) {
        if(x % 10 == 2) ++re;
        x /= 10;
    }return re;
}
signed main() {
    ll ans = 0;
    for(int i = 1; i <= 2020; ++i) {
        ans += cnt(i);
    }cout << ans << endl;//624
    return 0;
}

试题 B:既约分数

  • 题意:求分子、分母均在[1,2020]的区间内时,分子、分母的最大公约数为1的情况有多少种
  • 两重循环暴力即可,这里使用STL的__gcd()函数。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 7;
const int mod = 1000000009;
typedef long long ll;
signed main() {
    ll ans = 0;
    for(int i = 1; i <= 2020; ++i) {
        for(int j = 1; j <= 2020; ++j) {
            if(__gcd(i, j) == 1) ++ans;
        }
    }cout << ans << endl;//2481215
    return 0;
}

试题 C:蛇形填数

  • 题意:按蛇形填数,求第20行20列的数字是多少

  • 可以直接模拟。但是因为求的元素比较特殊,行号和列号一致,这里推一下通式,设:

  • 所以:,则:

  • 即:成立,累加可得:,其中所求为,代入可得答案为:

  • 下面给出模拟的代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 7;
const int mod = 1000000009;
typedef long long ll;
ll dp[64][64];
signed main() {
    int cnt = 1;
    for(int i = 1; i <= 50; ++i) {
        if(i & 1) {//从下往上
            for(int x = i, y = 1; x >= 1 && y <= i; --x, ++y) {
                dp[x][y] = cnt++;
            }
        }else {//从上往下
            for(int x = 1, y = i; x <= i && y >= 1; ++x, --y) {
                dp[x][y] = cnt++;
            }
        }
    }cout << dp[20][20] << endl;//761
    return 0;
}

试题 D:跑步锻炼

  • 题意:每天基础跑1km,若为周一或月初,加跑1km。求从[2000/1/1, 2020/10/1]间跑的路数

  • 直接模拟,遍历从2000/1/1到2020/10/1的所有天数,并且注意判断闰年和星期

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 1e5 + 7;
const int mod = 1e9 + 7;
int arr[][16] = {{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
signed main() {
    ll ans = 0;
    int cnt = 5;//0-6设为星期一~星期天
    for(int i = 2000; i <= 2020; ++i) {
        int mon = (i == 2020) ? 10 : 12;
        int flag = ((i % 4 == 0 && i % 100 != 0) || i % 400 == 0) ? 0 : 1;//0闰、1非闰
        for(int j = 1; j <= mon; ++j) {
            int day = arr[flag][j-1];
            if(mon == 10 && j == 10) day = 1;
            for(int k = 1; k <= day; ++k) {
                if(cnt == 0 || k == 1) ans += 2;
                else ++ans;
                cnt = (cnt + 1) % 7;
            }
        }
    }cout << ans << endl;//8879
    return 0;
}

试题 E:七段码

  • 题意:用七段码任意的凑出仅有一个联通块的数目
  • 因为图论学的比较多,这里就不直接去算了,上图暴力穷举搜索就好。
  • 假设从左上角开始,顺时针下每一个点分别记为,然后暴力穷举每一条边的存在情况,建无向图然后搜索联通块数目,然后统计合法的情况就可以了
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 1e5 + 7;
const int mod = 1e9 + 7;
vector <int> G[12];//6个节点
bitset<12> vis;
queue<int> q;
void bfs(int s) {
    while(!q.empty()) q.pop();
    q.push(s); vis[s] = 1;
    while(!q.empty()) {
        int from = q.front(); q.pop();
        for(int i = 0; i < G[from].size(); ++i) {
            int to = G[from][i];
            if(!vis[to]) {
                q.push(to);
                vis[to] = 1;
            }
        }
    }
}
signed main() {
    int ans = 0;
    for(int a = 0; a <= 1; ++a) {//因为情况比较少就直接开多重循环了,情况比较多可以转2进制枚举或者递归
        for(int b = 0; b <= 1; ++b) {
            for(int c = 0; c <= 1; ++c) {
                for(int d = 0; d <= 1; ++d) {
                    for(int e = 0; e <= 1; ++e) {
                        for(int f = 0; f <= 1; ++f) {
                            for(int g = 0; g <= 1; ++g) {
                                for(int i = 0; i <= 6; ++i) G[i].clear();
                                vis.reset();
                                if(a & 1) G[1].push_back(2), G[2].push_back(1);
                                if(b & 1) G[2].push_back(3), G[3].push_back(2);
                                if(c & 1) G[3].push_back(4), G[4].push_back(3);
                                if(d & 1) G[4].push_back(5), G[5].push_back(4);
                                if(e & 1) G[5].push_back(6), G[6].push_back(5);
                                if(f & 1) G[6].push_back(1), G[1].push_back(6);
                                if(g & 1) G[6].push_back(3), G[3].push_back(6);
                                int cnt = 0;
                                for(int i = 1; i <= 6; ++i) {
                                    if(vis[i] || G[i].size() == 0) continue;
                                    bfs(i);
                                    ++cnt;
                                }
                                if(cnt == 1) ++ans;
                            }
                        }
                    }
                }
            }
        }
    }cout << ans << endl;
    return 0;
}

试题 F:成绩统计

  • 题意:给定成绩序列,统计出及格率和优秀率
  • 直接模拟就好,记得开,及格人数中含优秀人数。四舍五入问题直接使用控制符指定位数会自动四舍五入
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 1e5 + 7;
const int mod = 1e9 + 7;
ll n;
signed main() {
    double ans1 = 0.0, ans2 = 0.0;//分别统计及格,优秀人数
    scanf("%lld", &n);
    for(int i = 1; i <= n; ++i) {
        int tmp;
        scanf("%d", &tmp);
        if(tmp < 60);
        else {
            ++ans1;
            if(tmp >= 85) ++ans2;
        }
    }
    printf("%.0lf%\n%.0lf%\n", ans1/n *100, ans2/n *100);
    return 0;
}

试题G:回文日期

  • 题意:计算给出日期的下一个回文日期、AB回文日期
  • 注意判断闰年
  • 这里考虑一下直接暴力,一个一个去判断日期是否回文,然后记录。因为实在太暴力了,而且还过了蓝桥杯的评测系统,所以提一句。可以考虑优化:将的回文序列构建出来,然后查表,或者考虑剪枝优化都可以
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 1e5 + 7;
const int mod = 1e9 + 7;
int year, month, day;
int arr[][16] = {{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
string turn(int num, int size) {//构造字符串
    string ans = "";
    while(num) {
        ans += num % 10 + '0';
        num /= 10;
    }
    while(ans.size() < size) ans += "0";
    reverse(ans.begin(), ans.end());
    return ans;
}

bool check1(string s) {//判断回文
    string rs = s;
    reverse(s.begin(), s.end());
    return (rs == s);
}
bool check2(string s) {//判断AB回文
    return (s[0] != s[1] && s[0] == s[2] && s[2] == s[5] && s[5] == s[7] && s[1] == s[3] && s[3] == s[4] && s[4] == s[6]);
}

signed main() {
    scanf("%4d%2d%2d", &year, &month, &day);
    string str1 = "", str2 = "";
    for(int m = month; m <= 12; ++m) {
        int dd = ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) ? 0 : 1;
        dd = arr[dd][m];
        for(int d = day; d <= dd; ++d) {
            if(m == month && d == day) continue;//当天不判断
            string tmp = turn(year, 4) + turn(m, 2) + turn(d, 2);
            if(check1(tmp)) {
                if(!str1.size()) str1 = tmp;
                if(!str2.size()) {
                    if(check2(tmp)) str2 = tmp;
                }
            }
        }
    }//判断当年

    for(int y = year + 1;!str1.size() || !str2.size(); ++y) {
        for(int m = 0; m < 12; ++m) {
            int dd = ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0)) ? 0 : 1;
            dd = arr[dd][m];
            for(int d = 1; d <= dd; ++d) {
                string tmp = turn(y, 4) + turn(m + 1, 2) + turn(d, 2);
                if(check1(tmp)) {
                    if(!str1.size()) str1 = tmp;
                    if(!str2.size()) {
                        if(check2(tmp)) str2 = tmp;
                    }
                }
            }
        }
    }cout << str1 << '\n' << str2 << '\n';
    return 0;
}

试题 H:字串分值和

  • 题意:查询字符串所有子串中,不同的字符的和的总和有多少
  • 我们去考虑每个字符在最后的总和上的贡献,我们可以看到样例中:对字串从开始的,第一个字符出现了次、对从第个开始的,第二个字符出现了。由此类推:第个出现了次,对于一次遍历过程中,我们可以将第个位置的字符对应的位置赋值为,对于此后的次循环中都加上它。
  • 在这里,我们不需要担心重复的问题,我们每更新一次,加一次和。当现在字符前有字符与它相同,则从此位开始的后续字串中,此位才存在贡献,前面存在相同的字符没有贡献
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 1e5 + 7;
const int mod = 1e9 + 7;
int arr[32];
signed main() {
    string s;
    cin >> s;
    long long sum = 0;
    for(int i = 0; i < s.size(); ++i) {
        arr[s[i] - 'a'] = i + 1;
        for(int i = 0; i < 26; ++i) sum += arr[i];
    }cout << sum << endl;
    return 0;
}

试题 I:平面切分

  • 题意:给定n条直线,求这些直线分出的面有多少个

  • 明显和直线分平面公式有关,直线分平面时,新增的平面数 = 这条线与前面的线的交点的数目 + 1

  • 很明显,题目中给出的直线可能会重复,有可能会存在交点,我们就要去重,然后找交点数目就可以给出答案了,我们这里算出交点,然后存放到中统计

  • 易得,注意统计时不要除

  • 我们对每一条线去统计它前面出现的线与它的交点数目,然后累加答案就好

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 1e3 + 7;
const int mod = 1e9 + 7;

set<pair<ll, ll> > line;
int n;
signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);//别用sc和read
    cin >> n;
    ll ans = 2;
    for(int i = 1; i <= n; ++i)  {
        int a, b;
        cin >> a >> b;
        int tmp = line.size();
        line.insert(make_pair(a, b));
        if(i > 1 && line.size() > tmp) {//如果是重复的点就不用再算
            if(i > 1 && line.size() > tmp) {
                set<pair<double, double> > node;
                for(set<pair<ll,ll> > :: iterator it = line.begin(); it != line.end(); ++it) {
                    ll A1 = a, A2 = it->first, B1 = b, B2 = it->second;
                    if(it->first == a || A1 - A2 == 0) continue;//去除平行线
                    double x = (double) (B2 - B1) / (A1 - A2);
                    double y = (double) (A1 * B2 - A2 * B1) / (A1 - A2);
                    node.insert(make_pair(x, y));
                }
                ans += node.size() + 1;
            }
        }
    }cout << ans << endl;
    return 0;
}

试题 J:字串排序

  • 题意:求出冒泡排序下交换次数为n的最小字典序字符串
  • 我们知道,要求字典序最小,首先要使得字符串长度最短,并且冒泡排序在最坏的情况下要交换次,所以设字符串长度为,则,所以字符串的长度为:
  • 我们可以由输入数据确定下来,那么直接用字符倒着排是不行的,因为最多只有个字母。所以我们不能直接去考虑倒排,要转换思路
  • 写不动了,等日后再补
全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务