第十一届蓝桥杯大赛第二场省赛
试题A 门牌制作
模拟即可
#include <iostream>
using namespace std;
int res;
int main()
{
for(int i = 1; i <= 2020; i ++)
{
int m = i;
while(m)
{
int t = m % 10;
if(t == 2) res ++;
m /= 10;
}
}
cout << res ;
return 0;
}
//答案:624
试题B 既约分数
填空题:gcd即可;
#include <iostream>
using namespace std;
int res;
int gcd(int a, int b) // 欧几里得算法
{
return b ? gcd(b, a % b) : a;
}
int main()
{
for(int i = 1; i <= 2020; i ++)
for(int j = 1; j <= 2020; j ++)
if(gcd(i,j) == 1) res ++;
cout << res ;
return 0;
}
//答案 : 2481215
试题C 蛇形填数
直接模拟即可, 因为只有两种情况
#include <bits/stdc++.h>
using namespace std;
int a[50][50],cnt=1;
int main()
{
//第几斜行
for(int i = 1; i <= 40; i++)
{
//奇数行, 斜行数是向左下递增
if(i & 1)
{
for(int x = i, y = 1; x >= 1 && y <= i; x--, y++)
a[x][y] = cnt++;
}
//偶数行, 斜行数是向右上递增
else
{
for(int x = 1, y = i; x <= i && y >= 1; x++, y--)
a[x][y] = cnt++;
}
}
printf("%d\n", a[20][20]);
return 0;
}
//答案:761
看见有的大佬把公式推出来了。。。
#include <iostream>
using namespace std;
int main()
{
//(n, n)的位置是(2n^2 - 2n + 1)
cout << 20 * 20 * 2 - 20 * 2 + 1;
return 0;
}
试题 D 跑步锻炼
千万注意写日历时对闰年的判断
#include <iostream>
using namespace std;
int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int d = 6, ans;
int main()
{
for(int i = 2000; i <= 2020; i ++)
{
if(i % 400 == 0 || (i % 4 == 0 && i % 100 != 0)) days[2] = 29;
else days[2] = 28; // 注意要还原!!!!!!
for(int j = 1; j <= 12; j ++)
{
for(int k = 1; k <= days[j]; k ++)
{
if(k == 1 || d % 7 == 1) ans += 2;
else ans ++;
d ++;
if(i == 2020 && j == 10 && k == 1) cout << ans;
}
}
}
}
//答案:8879
试题E 七段码
思路:并查集 && dfs
如果满足要求就加入集合最后判断点是否在集合里;
(数据范围有点小,如果dfs不熟悉的直接画图找。。。)
#include <iostream>
using namespace std;
const int N = 10;
int ans;
int p[N];
bool st[N];
int e[N][N];
//并查集板子
int find(int x)
{
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
void dfs(int u)
{
if(u == 8)
{
for (int i = 1; i <= 7; i ++) p[i] = i;
for (int i = 1; i <= 7; i ++)
for (int j = 1; j <= 7; j ++)
//如果有边相连,且都处于打开状态,则满足要求合并集合
if(e[i][j] && st[i] && st[j])
p[find(i)] = find(j);
int cnt = 0;
for (int i = 1; i <= 7; i ++)
//如果 i打开且在集合中
if(st[i] && p[i] == i)
cnt ++;
if(cnt == 1) ans ++;
return;
}
st[u] = 1; // 打开第 u 个二极管
dfs(u + 1);
st[u] = 0; // 关闭第 u 个二极管
dfs(u + 1);
}
int main()
{
//将所有联通边初始化成1
e[1][2] = e[1][6] = 1;
e[2][1] = e[2][7] = e[2][3] = 1;
e[3][2] = e[3][7] = e[3][4] = 1;
e[4][3] = e[4][5] = 1;
e[5][4] = e[5][7] = e[5][6] = 1;
e[6][1] = e[6][7] = e[6][5] = 1;
e[7][2] = e[7][3] = e[7][5] = e[7][6] = 1;
dfs(1);
cout << ans << endl;
return 0;
}
//答案 : 80
试题F 成绩统计
#include <iostream>
using namespace std;
int main()
{
int n;
cin >> n;
int s1 = 0,s2 = 0;
for(int i = 0;i < n;i ++ ){
int x;
cin >> x;
if(x >= 85) s2 ++;
if(x >= 60) s1 ++ ;
}
cout << int(s1 * 100.0 / n + 0.5)<< "%" <<endl;//加0.5四舍五入, 100.0将前面转成double型
cout << int(s2 * 100.0 / n + 0.5)<< "%" <<endl;
return 0;
}
试题G 回文日期
这题写了好久, 就是没办全过,所以只好看题解, 写了一种相对好写的版本
第一想法是按年枚举, 但是会有很多坑,比如样列, 但如果year + 1, 还会出现正确结果会在本年但在日期之后,索性直接就枚举整个日期,慢了点但也是能过的(还是太菜了啊。。。)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
using namespace std;
int months[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
bool check(int date) //判断日期是否合法,这可以当个日期板子背下来的
{
int year = date / 10000;
int month = date % 10000 / 100;
int day = date % 100;
if (month <= 0 || month >= 13) return false;
if (day == 0 || month != 2 && day > months[month]) return false;
if (month == 2)
{
int leap = (year % 4 == 0 && year % 100 != 0 || year % 400 == 0);
if (day > 28 + leap) return false;
}
return true;
}
bool check1(string s) //判断是否是回文日期
{
int len = s.size();
for(int i = 0, j = len - 1; i < j ; i++,j--) //双指针
{
if(s[i] != s[j]) return false;
}
return true;
}
bool check2(string s) //判断是否是ABABBABA 型的回文日期
{
if(check1(s))
{
if(s[0] != s[2] || s[1] != s[3] || s[0] == s[1]) return false;
return true;
}
}
int main()
{
int date, flag = 0;
cin >> date;
for(int i = date + 1; ;i++)
{
if(check(i))
{
string s = to_string(i);//数字直接转字符
if(check1(s) && !flag)
{
cout << i << endl;
flag = 1; //flag防止多次输出
}
if(check2(s))
{
cout << i << endl;
return 0;
}
}
}
return 0;
}
试题 H 字串分值和
思路:dp, 如图则问题转化为求解加入一个字符str[i]后对前面i−1个子串产生的影响,即可以看成是str[i]的贡献值,由于str[i]str[i]可以影响到它前面连续的不等同于str[i]的串的分值,并且每个子串的分值至多+1
故可以通过记录每个字符最后出现的位置pos,则多出str[i]后比f[i−1]f[i−1]多产生的分数就是i−1−pos
故f[i]=f[i−1]+i−pos
状态表示:集合:前i个字串 属性:数量
状态转移:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int N = 1e5 + 10;
char str[N];
typedef long long LL;
int pos[26];//记录每个字符最后一次出现的位置
int f[N];
int main() {
scanf("%s", str + 1);
int n = strlen(str + 1);
LL ans = 0;
for(int i = 1; i <= n; ++i) {
int t = str[i] - 'a';
f[i] = f[i - 1] + i - pos[t];
pos[t] = i;
ans += f[i];
}
printf("%lld\n", ans);
return 0;
}
试题 I 平面切分
直接题解指路了,我不可能有大佬写的好了指路
#include<iostream>
#include<set>//set为集合可以自动去重
using namespace std;
/*
1、重边不会影响区域数目。
2、每新加入一条边只要不是重边区域数目必定+1。
3、加入的新边如果与之前加入的边每有一个交点则区域数目+1。
*/
typedef pair<double,double> pdd;//存放A和B
set<pdd> lines;//存放直线因为可以去重,重边无贡献
int res=1; // 这里注意要从1开始,因为本就有一个部分
int cmp(double c,double d){//求此条直线与之前所有直线的交点个数
set<pdd> points;//交点的集合
pdd it;//交点
for(auto i=lines.begin();i!=lines.end();i++){
double a=i->first,b=i->second;
if(a!=c){//斜率不同则两条直线不重合
it.first=(d-b)/(a-c);//求交点的横坐标
it.second=c*it.first+d; //使用a和b求不通过??
points.insert(it);
}
}
return points.size();
}
int main(){
int n;
cin>>n;
while(n--){
double a,b;
cin>>a>>b;
int count1=lines.size();//count1为未加入现在这条边的总的边数
lines.insert({a,b});//将输入的x和y保存到line中
//如果现在加入的边是重边则无影响下面的if语句不会执行,如果不是重边则count1就不会等于增加后的边数
if(lines.size()!=count1){
res++;
res+=cmp(a,b);
}
}
cout<<res<<endl;
return 0;
}
试题 J 字串排序
#include<bits/stdc++.h>
using namespace std;
int V , len , now , cnt[27] , sum[27];
int get_max(int len){
return ((len - (len / 26 + 1)) * (len / 26 + 1) * (len % 26) + (26 - len % 26) * (len / 26) * (len - len / 26)) / 2;
}
bool check(int x , int n){
memset(cnt , 0 , sizeof(cnt));//cnt记录后面n个位置放置的对应字符数量
int add1 = 0 , add2 = 0;
for(int j = 26 ; j >= x + 1 ; j --) add1 += sum[j];//1~pos-1里边比当前插入字符大的字符数量
sum[x] ++ ;//当前字符数量增加1
for(int L = 1 ; L <= n ; L ++){
//ma保存最大逆序对个数 L-1-cnt[j]+num
//L-1-cnt[j]是当前字符之后的比当前字符小的字符数量的最大值
//num是1~pos+L-1前的比当前放置字符字典序大的字符数量
int ma = -1 , pos = 0 , num = 0;
for(int j = 26 ; j >= 1 ; j --){
if(L - 1 - cnt[j] + num > ma){
ma = L - 1 - cnt[j] + num;
pos = j;
}
num += sum[j];
}
add2 += ma , cnt[pos] ++;
}
if(now + add1 + add2 >= V) {
now += add1;
return true;
}
else {
sum[x] -- ;
return false;
}
}
signed main()
{
string ans = "";
cin >> V;
for(int i = 1 ; ; i ++) {
if(get_max(i) >= V){
len = i;
break ;
}
}
for(int i = 1 ; i <= len ; i ++){
for(int j = 1 ; j <= 26 ; j ++){
if(check(j , len - i)){
ans += char(j + 'a' - 1);
break ;
}
}
}
cout << ans << '\n';
return 0;
}