第十一届蓝桥杯大赛第二场省赛

第十一届蓝桥杯大赛第二场省赛

试题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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值