2021年 第十二届 蓝桥杯省赛 C/C++ B 组 学习总结

在这里插入图片描述

试题A:空间

解析:

  1. 内存 256MB = 256 * 1024 *1024 B
  2. 整数: 32位二进制数 32b = 4B
    (1MB = 1024 KB ; 1KB = 1024 B;1B = 8 b)
    化成字节 然后求解
#include <iostream>
using namespace std;
int main()
{
  // 请在此输入您的代码
  cout << 256 * 1024 * 1024 / 4;
  return 0;
}

在这里插入图片描述

试题B:卡片

解析:枚举数字 从1 开始递增 ,取余遍历数的每一位,判断是数字几,该数字的牌数减一,直到有牌耗尽,然后输出 i - 1

#include <iostream>
using namespace std;
int main()
{
  int a[10];  //存取10张牌的张数
  for(int i = 0; i < 10; i ++) a[i] = 2021; //把10张牌的张数都变为 2021
  //枚举数字 从1 开始递增 直到有牌被耗尽
  for(int i = 1; i; i ++)
  {
    int x = i; //数字i 存于 x 然后取余遍历 i的每一位
    while(x)
    {
      int y = x % 10;
      x /= 10;
      a[y] --;
      if(a[y] < 0) //如果有牌耗尽
      {
        cout << i - 1; //输出数字 i - 1,因为在拼i的时候就耗尽了 i是无法完成的
        return 0; 
      } 
    }
  }
  return 0;
}

在这里插入图片描述

试题C:直线

参考答案: 40257
解析:横直线和竖直线很容易统计,分别是21和20个。因此重点统计不同的斜直线个数。每条直线可用:y=kx+b表示,即一组不同的{k, b}代表不同的直线。所以本题的关键点在于用集合来存放并统计不同的斜直线个数
代码参考:作者:AI 菌

在这里插入图片描述

#include <iostream>
#include <vector>
#include <set>
using namespace std;

struct point
{
	int x; //横坐标
	int y; //纵坐标
};

int main()
{
	vector<point> p;//存放所有点
	for (int i = 0; i <= 19; ++i)
		for (int j = 0; j <= 20; ++j)
			p.push_back({i, j});

	int len = p.size();
	set<pair<double, double> > lines; //存放斜直线y=kx+b
	for (int i = 0; i < len; ++i)
	{
		for (int j = 0; j < len; ++j)
		{
			if (p[i].x != p[j].x && p[i].y != p[j].y) // 统计所有斜直线的情况
			{
				double k = (p[j].y - p[i].y) * 1.0 / (p[j].x - p[i].x);
				double b = (p[j].y * (p[j].x - p[i].x) - (p[j].y - p[i].y) * p[j].x) * 1.0 / (p[j].x - p[i].x);
				lines.insert(pair<double, double>(k, b));
			}

		}
	}
	cout << lines.size() + 20 + 21 << endl; // 总的直线=斜直线+横直线+竖直线
	return 0;
}

在这里插入图片描述

试题D:货物摆放

解析:用vector存储n的除数,然后枚举除数查看是否符合

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int INF = 0x3f3f3f3f;
int main(){
    long long N = 2021041820210418;
    int res = 0;
    vector<long long> ans;
    for(long long  i = 1; i*i <= N; i++){
        if(N % i == 0) {
            ans.push_back(i);
            ans.push_back(N/i);
        }
    } 
    //这里需要支持C++11
    for(auto i:ans)
        for(auto j:ans)
            for(auto k:ans)
                if(i*j*k == N) res++;
    cout<<res;            
    return 0;
}

在这里插入图片描述

试题E:路径

解析:枚举每个结点,查看它可以到达的地方,如果到达的路径长度较小,可以进行替换。最后输出到达2021的最短路径

#include <iostream>
#include <cstring>
using namespace std;
const int N = 2030;
int num[N]; //num[i]  表示 1 结点到 i 的最短路径 

int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a; 
}
int lcm(int a, int b)
{
    return a * b/ gcd(a,b) ;
}
int main()
{
  // 请在此输入您的代码
  memset(num, 0x3f3f3f3f, sizeof num);
  num[1] = 0;
  for(int i = 1; i <= 2021; i ++)
  {
      for(int j = 1; j <= 21; j ++)
      {
          int r = i + j;
          if(r <= 2021) num[r] = min(num[r], num[i] + lcm(i, r));
      } 
  }
  cout << num[2021];
  return 0;
}

在这里插入图片描述

试题F:时间显示

解析只需要显示时分秒,先化成秒为单位,然后对一天的时间进行取余,就可以得到那一天的时间和,然后取出时分秒即可

#include<iostream>
#include <cstdio>
 using namespace std;
 typedef long long LL;
 const LL day = 86400; //单位为秒day = 24 * 60 * 60;
int main()
{
    LL t; cin >> t;
    t /= 1000; // 化成秒
    LL time = t % day;
    int s = time % 60;
    int m = (t / 60) % 60;
    int h = time / 3600;
    printf("%02d:%02d:%02d", h, m, s) ; //%02d就是不足两位补零
    return 0;
}

在这里插入图片描述
在这里插入图片描述

试题G:砝码称重

解析:f[i][j]表示前 i 个砝码,凑出 j 重量 如果可以就为1 不可以为0
状态转移:1.不放 2.放在左边的盘子 3.在右边的盘子

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 110, M = 200010, B = M / 2;

int n, m;
int w[N];
bool f[N][M];

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]), m += w[i];

    f[0][B] = true;
    for (int i = 1; i <= n; i ++ )
        for (int j = -m; j <= m; j ++ )
        {
            f[i][j + B] = f[i - 1][j + B];
            if (j - w[i] >= -m) f[i][j + B] |= f[i - 1][j - w[i] + B];
            if (j + w[i] <= m) f[i][j + B] |= f[i - 1][j + w[i] + B];
        }

    int res = 0;
    for (int j = 1; j <= m; j ++ )
        if (f[n][j + B])
            res ++ ;
    printf("%d\n", res);
    return 0;
}

在这里插入图片描述

试题H:杨辉三角形

解析:首先需要知道必要的规律就是我们可以根据行号和列号求出对应数的值和序号,然后从第16列开始枚举,因为C(32,16) > 109
为什么是32 ?16作为这一行的最大数,就需要16在正中间。 16*2 = 32
C函数就是计算C(a, b)

在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int n;
LL C(int a, int b)   //计算C(a,b)
{
    LL res = 1;
    for(int i = a, j = 1; j <= b; i --, j ++)
    {
        res = res * i / j;
        if(res > n)
            return res;     // 大于n已无意义,且防止爆LL
    }
    return res;
}
bool check(int k)
{
     int minr = 2 * k, maxr = n; //最小行 和 最大行
     while(minr <= maxr)
     {
         int mid = (minr + maxr) / 2 ;
         if(C(mid, k) == n) {
             cout << (LL)(mid + 1) * mid / 2 + k + 1 << endl; //1ll 是1LL转化long long 类型数据
             return true;
         }
         else if(C(mid, k) > n) maxr = mid - 1;
         else minr = mid + 1;
     }
     return false;
}
int main()
{
    cin >> n;  //输入一个数 
    // 从第16斜行枚举
    for(int i = 16; ; i--)
        if(check(i))
            break;
    return 0;
}

在这里插入图片描述
在这里插入图片描述

试题I:双向排序

解析:这个直接sort() ,可以通过60% 的数据,要优化代码的话就需要去掉多余的操作,还有一些规律。
通过全部数据的代码双向排序 作者: 要挥剑了 ,

//只能通过部分数据的代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int a[N];
int main()
{
  int n, m;
  cin >> n >> m;
  for(int i = 0;i < n; i ++) a[i] = i + 1;

  while(m --)
  {
  	int p, q;
  	cin >> p >> q;
  	if(p == 0) sort(a, a + q, greater<int>()); //降序
  	else{
  		sort(a + q - 1, a + n,less<int>());//升序
  	}
  }
  for(int i = 0;i < n; i ++) cout << a[i] << " ";
  return 0;
}

在这里插入图片描述

试题J:括号序列

解析:
在这里插入图片描述

//括号序列 
#include<iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
 using namespace std;
 const int N = 5010, MOD = 1e9 + 7;
typedef long long LL;
 char str[N];
 int n; 
 LL f[N][N];//考虑前i个括号 左括号比右括号大j的方案数 
LL work()
{
	memset(f, 0, sizeof f);
	f[0][0] = 1; //考虑前0个括号 左括号比右括号大0 添加方案只有一种:不添加 
	for(int i = 1; i <= n; i ++ )
	{
		if(str[i] == '(') //如果第i个字符为 (  
		{
			//前i个字符下的 左括号比右括号大j的情况都更新 
			for(int j = 1; j <= n; j ++) 
				f[i][j] = f[i - 1][j - 1];
		}
		else{
			//第i个字符为右括号  
			f[i][0] = (f[i - 1][1] + f[i - 1][0]) % MOD; //0需要特殊处理 否则 j-1出错 
			for(int j = 1; j <= n; j ++) 
				f[i][j] = (f[i - 1][j + 1] + f[i][j - 1]) % MOD;
		} 
	} 
	//因为需要尽可能少添加括号使序列合法 所以从小开始找
	for(int i = 0; i <= n; i ++) 
	{
		if(f[n][i]) return f[n][i];
	}
	return -1;
}
int main()
{
	scanf("%s", str + 1);
    n = strlen(str + 1);
    LL l = work();
    reverse(str + 1, str + 1 + n); //翻转序列 然后左括号变右括号再算右括号的添加方案 
    for(int i = 1; i <= n; i ++)
    {
    	if(str[i] == '(' ) str[i] = ')';
    	else str[i] = '(';
    }
    LL r = work();
    printf("%lld\n",l * r % MOD);
    
	return 0;
}
 

代码参考:括号序列
注:有部分代码来源网络,由于整理时间太长,重新找的出处有点难,如果侵犯到了原作者的权益,请联系我进行修改、删除、或注明出处,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值