第十一届蓝桥杯B组省赛题目及题解

A

在这里插入图片描述
答案:624

思路:枚举

代码:

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

int check(int x){
    int cnt = 0;
    while(x > 0){
        int p =x %10;
        x/=10;
        if(p == 2)
            cnt++;
    }
    return cnt;
}
int main()
{
    int ans = 0;
    for(int i = 1; i <= 2020 ;++i){
        ans += check(i);
    }
    cout<<ans<<endl;
    return 0;
}

B

在这里插入图片描述
答案:2481215

思路:枚举

代码:

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

int gcd(int a,int b){
    if(b == 0) return a;
    return gcd(b,a % b);
}
int main()
{
    ll ans = 0;
    // cout<<gcd(15,20);
    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

思路:找规律,第i行i列依次为1,5,13,25,41,第 i i i个数与 i − 1 i-1 i1个数的差为 ( i − 1 ) × 4 (i-1)\times4 i1×4

代码

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

int main()
{
    int ans = 1;
    for(int i = 2;i <= 20; ++i){
        ans += 4 * (i - 1);
    }
    cout<<ans;
    return 0;
}

D

在这里插入图片描述

答案:8879

思路
首先启动计算器,先看一下间隔时间,加一天共计7580天。
在这里插入图片描述
跑步公里就是总天数加上周一的天数,加上每个月1号不是周一的天数。

容易发现,设某天与第一天间隔x天,若x%7 == 2,说明这天为星期一。就可以O(1)的判断某天是否为周一。

枚举每个月的1号,算出是开始后的第几天,再判断这天是否为周一。

同时计算任意两个日期的总天数还可以用上面计算器算的天数验证结果是否正确。

补充
蓝桥杯经常有年份相关的,闰年的计算方式是:

1.普通年份能被4整除,且不能被100整除的,是闰年。(如2004年就是闰年)

2.世纪年份能被400整除的是闰年。(如2000年是闰年,1900年不是闰年)

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int  day[12] = {31,28,31,30,31,30,31,31,30,31,30,31};

struct  da
{
    int y,m,d;
    da() {}
    da(int a,int b,int c): y(a),m(b),d(c){}
};

int check(int y)//判断闰年
{
    if((y % 100 == 0) && (y % 400 == 0))
        return 1;
    if((y % 100 != 0) && (y % 4 == 0))
        return 1;
    return 0;
}

int cal(da s,da e)//两个日期的天数
{
    int d = 0;
    //首年
    if(s.y != e.y){
        for(int i = s.m ;i <= 12; ++i){
            if(check(s.y))
            {
                if(i == 2){
                    if(i == s.m )
                        d +=  29 - s.d + 1;
                    else d += 29;
                }
                else{
                    if(i == s.m )
                        d +=  day[i - 1] - s.d + 1;
                    else d += day[i - 1];
                }
            }
            else{
                if(i == s.m )
                    d +=  day[i - 1] - s.d + 1;
                else d += day[i - 1];
            }
        }
    }
    // cout<<d<<endl;
    for(int i = s.y + 1 ;i < e.y; ++i){
        if(check(i))
            d += 366;
        else d += 365;
    }
    // cout<<d<<endl;
    for(int i = 1; i <= e.m ; ++i){
        if(check(e.y) )
        {
            if(i == 2){
                if(i == e.m )
                    d +=  e.d;
                else d += 29;
            }
            else{
                if(i == e.m )
                    d +=  e.d;
                else d += day[i - 1];
            }
        }
        else{
            if(i == e.m )
                    d +=  e.d;
            else d += day[i - 1];
        }
    }
    return d;
}
int main()
{
    da s(2000,1,1),e(2020,10,1);
    int ans =cal(s,e);
    // cout<<ans<<endl;
    int cnt = ans;
    for(int i = 1;i <= cnt; ++i){
        if( (i-1) % 7 == 2)
            ans++;
    }
    // cout<<ans<<endl;
    for(int i = s.y; i <= e.y; i++){
        for(int j = 1;j <= 12; j++){
            if(i == e.y && j > e.m)
                break;
            da u(i,j,1);
            cnt = cal(s,u);
            if((cnt - 1) % 7 != 2){
                // cout<<u.y<<" "<<u.m<<" "<<u.d<<endl;
                ans++;
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

E

在这里插入图片描述
答案:80

思路:搜索+并查集
对于每个灯有选或不选两种状态,一共是 2 7 2^7 27状态,对于每个状态判断当前选的是否连通。

如果选的正好有一个连通块,答案加一。

对于每个状态,用并查集进行两两连通,数有几个连通块即可。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int pre[8];
int a[8][8];
int vis[8],ans =0;
// abcdefg
// 1234567
void init(){
    a[1][6] = a[1][2] = 1;
    a[2][1] = a[2][7] = a[2][3] = 1;
    a[3][4] = a[3][7] = a[3][2] = 1;
    a[4][3] = a[4][5] = 1;
    a[5][4] = a[5][7] = a[5][6] = 1;
    a[6][1] = a[6][7] = a[6][5] = 1;
    a[7][6] = a[7][2] = a[7][3] = a[7][5] = 1;
}

int find(int x){
    if(pre[x] == x )
        return  x ;
    else return pre[x] = find(pre[x]);
}

void dfs(int pos)
{
    if(pos > 7){
        for(int i = 1;i <= 7; i++)
            pre[i] = i;
        for(int i = 1;i <= 7; i++){
            for(int j = 1;j <= 7; j++){
                if(a[i][j] && vis[i] && vis[j]){
                    int fa = find(i),fb = find(j);
                    // cout<< fa<<" "<<fb<<endl;
                    if(fa != fb)
                        pre[fa] = fb;
                }
            }
        }
        int cnt = 0;
        for(int i = 1;i <= 7;++i){
            if(vis[i] && (pre[i] == i))
                cnt++;
        }
        if(cnt == 1)
            ans++; 
        return;
    }
    vis[pos] = 1;
    dfs(pos + 1);
    vis[pos] = 0;
    dfs(pos + 1);
}
int main()
{
    init();
    dfs(1);
    cout<<ans<<endl;
    return 0;
}

F 成绩统计

在这里插入图片描述

题目链接:https://www.acwing.com/problem/content/2874/
代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
int a[maxn];

int main()
{
    int n,cnt1 = 0,cnt2 = 0;
    cin>>n;
    for(int i = 1;i <= n; ++i){
        cin>>a[i];
        if(a[i] >= 60) cnt1++;
        if(a[i] >= 85) cnt2++;
    }
    int a = cnt1 * 1.0 / n * 1000;
    int b = cnt2 * 1.0 / n * 1000;
    // cout<<a<<" "<<b<<endl;
    if(a % 10>= 5)
        cout<<a / 10+ 1<<"%"<<endl;
    else 
        cout<<a / 10 <<"%"<<endl; 
    if(b % 10 >= 5)
        cout<<b / 10  + 1<<"%"<<endl;
    else 
        cout<<b / 10<<"%"<<endl;    
    return 0;
}

或者直接用四舍五入函数

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
int a[maxn];

int main()
{
    int n,cnt1 = 0,cnt2 = 0;
    cin>>n;
    for(int i = 1;i <= n; ++i){
        cin>>a[i];
        if(a[i] >= 60) cnt1++;
        if(a[i] >= 85) cnt2++;
    }
    double a = cnt1 * 1.0 / n * 100;
    double b = cnt2 * 1.0 / n * 100;
    cout<<round(a)<<"%"<<endl;
    cout<<round(b)<<"%"<<endl;
    return 0;
}

补充:

c++中的一些函数:

floor : 不大于自变量的最大整数

ceil: 不小于自变量的最大整数

round:四舍五入到最邻近的整数

G

在这里插入图片描述
链接:https://www.acwing.com/problem/content/2870/

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
int mo[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
int n[4];

int run(int y){
    if(y % 100 == 0 && y % 400 == 0)
        return 1;
    if(y % 100 != 0 && y % 4 == 0)
        return 0;
    return 1;
}

string change(int x)
{
    string s="";
    while(x > 0){
        int p =x % 10;
        s += (p +'0');
        x /= 10;
    }
    reverse(s.begin(),s.end());
    return s;
}

int main()
{
    // int s;
    string s,ans1 = "",ans2 = "";
    cin>>s;
    int m = (s[4] - '0') * 10 + s[5] - '0';
    int d = (s[6] - '0') * 10 + s[7] - '0';
    int y = (s[0] - '0') * 1000 + (s[1] - '0') * 100 + (s[2] - '0') * 10 + s[3] - '0';
    
    int yy,mm,dd;
    for(int i = y; ; i++){
        string s1 = "",s2 = "";
        mo[1] = 28;
        s1 = change(i);
        mm = (s1[3] - '0') * 10 + s1[2] - '0';
        dd = (s1[1] - '0') * 10 + s1[0] - '0';
        if(run(i)) mo[1] = 29;
        // cout<<i<<endl;
        // cout<<mm<<" "<<dd<<endl;
        if(mm > 12 || mm <= 0 || dd > mo[mm - 1] || dd <= 0 )
            continue;
        if(y == i && ( mm < m || (mm == m && dd <= d)))
            continue;
        s2 += s1;
        reverse(s1.begin(),s1.end());
        s2 +=  s1;
        if(s2[0] == s2[2] && s2[1] == s2[3] && s2[0] != s2[1]){
            ans2 += s2;
            if(ans1 == "") ans1 += s2;
            cout<<ans1<<endl;
            cout<<ans2<<endl;
            break;
        }
        else{
            if(ans1 == "") ans1 += s2;
        }
    }
    return 0;
}

H

在这里插入图片描述链接https://www.acwing.com/problem/content/2875/

思路

暴力:

对于这个题场上可以考虑暴力骗分大法,枚举所有区间,然后在暴力的基础上用前缀和优化,预处理每一位的字母出现次数前缀。这样每个区间每个字母的个数可以O(1)查询。

然后遍历所有区间,时间复杂度是 O ( n 2 ) O(n^2) O(n2),能拿到一半的分。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6+5;
int l[maxn][25];

int main()
{
    string s;
    int ans = 0,cnt = 0;
    cin>>s;
    for(int i = 0;i <s.length(); ++i){
        if(i != 0)
            for (int j = 0; j < 25; j++)
                l[i][j] = l[i - 1][j];
        l[i][s[i] - 'a']++;
    }
    for(int i = 0;i < s.length(); i++){
        for (int j = i ;j <s.length(); ++j){
            cnt = 0;
            for(int k = 0;k < 25 ;k++){
                if(l[j][k] == 0)
                    continue;
                if(i == 0)
                    cnt++;
                else if(l[j][k] - l[i - 1][k] > 0)
                    cnt++;
            }
            ans += cnt;
        }
    }
    cout<<ans<<endl;
    return 0;
}

正解: dp,时间复杂度O(n)
d p [ i ] dp[i] dp[i] 表示以第i位为结尾的串的权值和

容易发现如果这一位字母从未出现过,那么以第i位为结尾的串每个串都会出现新的字母,每个串都要加1,就是 d p [ i ] = d p [ i − 1 ] + i + 1 ; dp[i] = dp[i - 1] + i +1; dp[i]=dp[i1]+i+1;

如果这一位字母出现过,记上一次出现的位置为last,包含last的以第i位为结尾的串每个串不变,不包含的每一个串要加一,也就是 d p [ i ] = d p [ i − 1 ] + i − l a s t dp[i] = dp[i - 1] + i - last dp[i]=dp[i1]+ilast

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6+5;
map<char,int> last;
int dp[maxn];

int main()
{
    string s;
    ll ans = 0;
    cin>>s;
    dp[0] = 1;
    last[s[0]] = 0;
    for(int i = 1;i < s.length(); i++){
        if(last.find(s[i]) == last.end()){
            dp[i] = dp[i - 1] + i  +1; 
            last[s[i]] = i;
        }
        else{
            dp[i] = dp[i - 1] + i - last[s[i]];
            last[s[i]] = i;
        }
        // cout<<dp[i]<<" ";
    }
    // cout<<endl;
    for(int i = 0;i < s.length(); ++i)
        ans += dp[i]* 1ll;
    cout<<ans<<endl;
    return 0;
}

I

在这里插入图片描述
链接https://www.acwing.com/problem/content/2876/

思路
一半的数据这么小,骗分骗分,不骗不是人,枚举小于3的情况,可以骗30%的分。

#include <bits/stdc++.h>
using namespace std;
#define PII pair<double,double> 
typedef long long ll;
const int maxn = 1e6+5;
int a[maxn],b[maxn];
PII po[maxn];
int n;
PII cal(int i,int j)
{
    double x1,y1;
    x1 = (b[j] - b[i]) * 1.0 / (a[i] - a[j]);
    y1 = a[i] * 1.0 * x1 + b[i];
    return {x1,y1};
}
int main()
{
    int ans,f = 0;
    cin>>n;
    for(int i = 1;i <= n; ++i)
        cin>>a[i]>>b[i];
    for(int i = 1;i <= n; ++i)
        if(a[i] != a[1] || b[i] != b[1]){
            f = 1;break; 
         }
    if(n == 1|| f == 0) ans = 2;
    f = 0;
    for(int i = 1;i <= n; ++i)
        if(a[i] != a[1]){
            f = 1;break; 
        }
    if(f == 0)
        ans = n + 1;
    else{
        if(n == 2){
            if(a[1] != a[2]) ans = 4;
            else
                if(b[1] != b[2]) ans = 3;
        }
        if(n == 3){
            int cnt = 0;
            f =1;
            for(int i = 1;i <= n; i++){
                for(int j = i + 1;j <= n;++j ){
                    po[++cnt] = cal(i,j);
                    if(po[cnt] != po[1])
                        f = 0;
                }
            }
            if(f)
                ans =6;
            else
            ans =7;
        }
    }
    cout<<ans<<endl;
    return 0;
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值