2019年蓝桥杯省赛B组C++题解(无I、J题)

A:门派制作

答案:624
——————————————————————————————————

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int solve(int n){
int ans = 0;
while(n > 0){
if(n % 10 == 2){
ans++;
}
n /= 10;
}
return ans;
}
int main(){
int ans = 0;
for(int i = 1; i <= 2020; ++i){
ans += solve(i);
}
cout << ans << endl;
return 0;
}

B:既约分数

答案:2481215
——————————————————————————————————

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int gcd(int a, int b){
return b ? gcd(b, a%b) : a;
}
int main(){
int 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;
return 0;
}

C:蛇形填数

答案:761

  • 这道题其实一个公式就可以得到答案了。。。

分析

  1. 将脑袋向左歪45°再看看这个矩阵,把这个新的矩阵称为“斜矩阵”吧

    1  2  6  7 15 16
    3  5  8  14 17
    4  9  13 18
    10 12 19 25 
    11 20 24
    21 23
    22
  2. 斜着从上到下数的话,第i行共有i个数;
    原矩阵的某k行k列,就是斜矩阵的第j行第j/2+1个数(j为奇数)
    [3][3] = 13即第5行第3个数; [4][4] = 25即第7行第4个数
    那么k和j有什么关系呢,j = 2*k-1

  3. 于是[20][20]就可以很愉快地得出是斜距阵的第39行第20个数

  4. 数是累加的,设斜距阵前38行共ans个数,那么本题答案就是ans+20

  5. 再多看看就会发现,求前n行有多少个数,不就是等差数列前n项和吗?
    于是ans + 20 = (1+38)*38/2 + 20 = 761
    再多说一句都是废话了~

D:跑步锻炼

答案:8879

能用笔算的就不上代码,能上代码的就不要用excel
——————————————————————————————————

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int months[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int main(){
int y = 2000, m = 1, d = 1;
int ans = 0, week = 5; //week取0~6即周一~周日
while(y != 2020 || m != 10 || d != 1){
if((y%4==0&&y%100!=0) || y%400==0){ //闰年
months[2] = 29;
}else{
months[2] = 28;
}
d++;
if(d > months[m]){
d = 1;
m++;
}
if(m == 13){
m = 1;
y++;
}
week = (week+1) % 7;
if(week == 0 || d == 1){
ans++;
}
ans++;
}
cout << ans+2 << endl;
return 0;
}

E:七段码

答案:80

分析

  1. 本题的思路其实来源于2016年蓝桥B组C++的G题:剪邮票一题
    传送门:
  2. 将七段码看作七张牌,从中任选k张牌(k>1),然后判断k张牌是否相连,若相连,则满足题意
    ***注:为避免重复,选牌时排列不组合,共$\sum{C_{7}^{k}}(k = 1,2,…,7)$种可能的排列

——————————————————————————————————

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

int ans = 0;
int b[8];
string s = "00000000";
int a[8][8];
vector<int> v;
void line(int x, int y){
a[x][y] = 1;
}
void init(){
line(1, 2); line(2, 1);
line(1, 3); line(3, 1);
line(2, 4); line(4, 2);
line(3, 4); line(4, 3);
line(2, 5); line(5, 2);
line(3, 6); line(6, 3);
line(4, 5); line(5, 4);
line(4, 6); line(6, 4);
line(5, 7); line(7, 5);
line(6, 7); line(7, 6);
}
void dfs_1(int x){ //从一点出发,走遍能够走的点
s[x] = '0';
for(int i = 1; i <= 7; ++i){
if(i == x){
continue;
}
if(s[i] == '1' && a[x][i] == 1){
dfs_1(i);
}
}
}
bool solve(){ //判断是否相连
s = "00000000";
for(int i = 0; i < v.size(); ++i){ //选中的牌标记为1
s[v[i]] = '1';
}
dfs_1(v[0]);
if(s.find('1') == string::npos){ //若所有的标记都抹掉了,则连通
return true;
}else{
return false;
}
}
void dfs(int k, int step){ //选k张牌
if(step >= 1){
if(solve()){
ans++;
}
// for(int i = 0; i < v.size(); ++i){
// cout << v[i];
// }
// cout << endl;
}
for(int i = k; i <= 7; ++i){
if(!b[i]){
b[i] = 1;
v.push_back(i);
dfs(i, step+1);
v.pop_back();
b[i] = 0;
}
}
}
int main(){
init();
dfs(1, 0);
cout << ans << endl;
return 0;
}

F:成绩统计

题目来源:·【Acwing】·成绩统计

——————————————————————————————————

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
nt main(){
int n;
double ans1 = 0, ans2 = 0;
scanf("%d", &n);
for(int i = 0; i < n; ++i){
int m;
scanf("%d", &m);
if(m >= 60){
ans1++;
}
if(m >= 85){
ans2++;
}
}
printf("%.0lf%\n%.0lf%\n", ans1*100/n, ans2*100/n);
return 0;
}

G:回文日期

题目来源:·【Acwing】·回文日期
分析

  1. 直接从输入的日期n开始累加,上限即99991231
  2. 增加非法日期的判断,以及回文日期、ABABBABA型日期的判断即可

***注:建议不要用yy,mm,dd标记年月日,然后一天天累加,似乎这是取巧的办法,
这样虽然得到的都是合法日期,但后面可能涉及到string的拼接,这样效率反而变慢了,
笔者之前这样做的,亲测超时呦~
——————————————完整代码————————————————

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <iostream>
#include <algorithm>
#include <sstream>
using namespace std;

string ss1 = "0", ss2 = "0"; //记录两个日期
string judge1(string s){ //回文日期
//cout << s << endl;
for(int i = 0; i < s.length()/2; ++i){
if(s[i] != s[s.length()-1-i]){
return "0";
}
}
return s;
}
string judge2(string s){ //ABABBABA型日期
//cout << " "<<s << endl;
if(judge1(s) == s && s[0] == s[2] && s[1] == s[3] && s[0] != s[1]){
return s;
}else{
return "0";
}
}
string toString(int n){
stringstream ss;
ss << n;
return ss.str();
}
int months[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
void solve(int n){
int y = n / 10000, m = n % 10000 / 100, d = n % 100;
if((y%4==0&&y%100!=0) || y%400==0){ //闰年
months[2] = 29;
}else{
months[2] = 28;
}
if(m == 0 || m > 12 || d == 0 || d > 31 || months[m] < d){
return;
}
string s = toString(n);
if(ss1 == "0")
ss1 = judge1(s);
if(ss2 == "0" && ss1 != "0")
ss2 = judge2(s);
}
int main(){
int n;
scanf("%d", &n);
for(int i = n+1; i < 99991231; ++i){
solve(i);
if(ss1 != "0" && ss2 != "0")
break;
}
cout <<ss1 <<endl <<ss2 <<endl;
return 0;
}

H:子串分值和

题目来源:·【Acwing】·子串分值和

子串 f值
a 1
ab 2
aba 2
abab 2
ababc 3
b 1
ba 2
bab 2
babc 3
a 1
ab 2
abc 3
b 1
bc 2
c 1

分析

  1. 暴力的解法就不提了,观察发现,对于输入串s,每个s[i]真实统计的次数是有规律的
  2. 如s[0]真实统计的次数为1*5;s[2]真实统计的次数为2*3;一句话概括就是
    (s[i]当前位置到s[i]上一次出现的距离)*(s[i]到末端的距离)

***注:ACwing中的训练样例实在爆炸,结果只通过了一半数据。。。
——————————————完整代码————————————————

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <algorithm>

typedef long long ll;
using namespace std;

int word[26];
int main(){
string s;
cin >> s;
int n = s.length();
int a[n];
fill(word, word+26, -1);
fill(a, a+n, -1);
ll ans = 0;
for(ll i = 0; i < n; ++i){
ans += (i - word[s[i]-'a']) * (n - i);
word[s[i]-'a'] = i;
}
cout << ans << endl;
return 0;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!