2021第十二届4月蓝桥杯大赛软件类B组C/C++省赛题解(已更新至第I题)

好多题目事后发现错了,真的血亏TAT。

试题 A:空间(结果填空)

题意
在这里插入图片描述

做法:1ll * 256 * 1024 * 1024 * 8 / 32

答案:67108864


试题 B:卡片(结果填空)

题意
在这里插入图片描述

做法:这题一定要注意是最多能从1拼到多少。

代码

#include<bits/stdc++.h>
using namespace std;
int a[10];
int solve(int x) {
  while(x) {
    if(!a[x%10]) return 0;
    --a[x%10];
    x /= 10;
  }
  return 1;
}
int main() {
  for(int i = 0; i < 10; ++i) a[i] = 2021;
  int cnt = 1;
  while(1) {
    if(!solve(cnt)) break;
    ++cnt;
  }
  cout << cnt - 1;
  return 0;
} 

答案:3181


试题 C:直线(结果填空)

题意
在这里插入图片描述
做法:枚举每两个点,最后check函数判重,暴力写法。

代码

#include<bits/stdc++.h>
#define eps 1e-9
using namespace std;
const int N = 1e7;
struct xx {
  double k, b;
}p[N];
int tot;
void check(double x1, double y1, double x2, double y2) {
  double k, b;
  k = (y2-y1)/(x2-x1); b = y1 - k*x1;
  int f = 1;
  for(int i = 1; i <= tot; ++i) {
    if(fabs(k - p[i].k) < eps && fabs(b - p[i].b) < eps) {
      f = 0; break;
    }
  }
  if(f) p[++tot] = xx {k, b};
}
int main() {
  int n = 20, m = 21;
  for(double x1 = 0; x1 < n; ++x1) {
    for(double y1 = 0; y1 < m; ++y1) {
      for(double x2 = 0; x2 < n; ++x2) {
        for(double y2 = 0; y2 < m; ++y2) {
          if(x1 == x2 || y1 == y2) continue;
          check(x1, y1, x2, y2);
        }
      }
    }
  }
  cout << tot+n+m;
  return 0;
}

答案:40257


试题 D:货物摆放(结果填空)

题意
在这里插入图片描述
做法:正确做法是质因子分解把,因为是填空题于是用暴力写了。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
vector<LL> ve;
int main() {
	LL n = 2021041820210418;
	LL sq = sqrt(n);
	for(LL i = 1; i <= sq; ++i) {
	  if(n%i == 0) {
	    ve.push_back(i);
	    if(i != n/i) ve.push_back(n/i);
    } 
  }
  sort(ve.begin(), ve.end());
  int nn = ve.size(), res = 0;
	for(int i = 0; i < nn; ++i) {
	  for(int j = 0; j < nn; ++j) {
	    if(ve[j] > n/ve[i]) break;
      for(int k = 0; k < nn; ++k) {
	      if(ve[i] > n/(ve[j]*ve[k])) break;
	      if(ve[i]*ve[j]*ve[k] == n) ++res;
      }
    }
  }
  cout << res;
  return 0;
}

答案:2430

试题 E:路径(结果填空)

题意
在这里插入图片描述

做法:建边+dijsktra跑最短路。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL, int> pli;
const int N = 1e4;
const int M = 1e7;
struct xx {
  int to, nxt;
  LL w;
}e[M];
int head[N], tot;
void Add(int u, int v, LL w) {
  e[++tot] = xx{v, head[u], w};
  head[u] = tot;
}
LL dis[N];
int vis[N];
priority_queue< pli, vector<pli>, greater<pli> > pq;
void dij() {
  for(int i = 0; i <= 2021; ++i) dis[i] = 1e18, vis[i] = 0;
  dis[1] = 0;
  pq.push(make_pair(dis[1], 1));
  int u, v;
  while(!pq.empty()) {
    u = pq.top().second; pq.pop();
    if(vis[u]) continue;
    vis[u] = 1;
    for(int i = head[u]; i; i = e[i].nxt) {
      v = e[i].to;
      if(dis[v] > dis[u] + e[i].w) {
        dis[v] = dis[u] + e[i].w;
        pq.push(make_pair(dis[v], v)); 
      }
    }
  }
}
int main() {
  for(int i = 1; i <= 2021; ++i) {
    for(int j = i+1; j <= 2021; ++j) {
      if(j-i <= 21) {
        LL w = i*j/__gcd(i, j);
        Add(i, j, w); Add(j, i, w);
      }
    }
  }
  dij();
  cout << dis[2021];
  return 0;
}

答案:10266837


测评网站:Acwing,更新中~
以下代码保证正确性,通过了Acwing的数据。

试题 F:时间显示(程序设计)

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

做法:首先取余一天的时间,最后剩下不到一天的时间再分别除以时分秒,每步取余就可以得到啦。

代码

#include<bits/stdc++.h>
using namespace std;
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define _for(n,m,i) for (int i = (n); i < (m); ++i)
#define _rep(n,m,i) for (int i = (n); i <= (m); ++i)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define PI acos(-1)
#define eps 1e-8
#define rint int
typedef long long LL;
typedef pair<LL, int> pli;
typedef pair<int, int> pii;
typedef pair<double, int> pdi;
typedef pair<LL, LL> pll;
typedef pair<double, double> pdd;
typedef map<int, int> mii;
typedef map<char, int> mci;
typedef map<string, int> msi;
template<class T>
void read(T &res) {
  int f = 1; res = 0;
  char c = getchar();
  while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
  while(c >= '0' && c <= '9') { res = res * 10 + c - '0'; c = getchar(); }
  res *= f;
}
const int ne[8][2] = {1, 0, -1, 0, 0, 1, 0, -1, -1, -1, -1, 1, 1, -1, 1, 1};
const int INF = 0x3f3f3f3f;
const int N = 3e5+10;
const LL Mod = 1e9+7;
const int M = 1e6+10;
int main() {
  LL t; cin >> t;
  LL day_ms = 24 * 60 * 60 * 1000, h_ms = 60 * 60 * 1000, m_ms = 60 * 1000, s_ms = 1000;
  t %= day_ms;
  int h, m, s;
  h = t / h_ms; t %= h_ms;
  m = t / m_ms; t %= m_ms;
  s = t / s_ms; t %= s_ms;
  printf("%02d:%02d:%02d\n", h, m, s);
  return 0;
}

试题 G:砝码称重(程序设计)

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

做法:动态规划,这道题可以转换为一个问题,对这n个数字相互之间可以进行加法和减法,每个数字只能用一次,问可以得到多少个不同的数字。那么dp[i]设置有两个状态,0或者1,对于每个a[i],进行两次动态规划,第一次dp[j]=max(dp[j],dp[j-a[i]]),表示只有加法的时候j是否能被得到(0得不到,1能得到),第二次dp[j]=max(dp[j],dp[j+a[i]])表示在加法的基础上增加减法后j是否能被得到,最后统计1至Max中有多少个j,满足dp[j]=1。

代码

#include<bits/stdc++.h>
using namespace std;
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define _for(n,m,i) for (int i = (n); i < (m); ++i)
#define _rep(n,m,i) for (int i = (n); i <= (m); ++i)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define PI acos(-1)
#define eps 1e-8
#define rint int
typedef long long LL;
typedef pair<LL, int> pli;
typedef pair<int, int> pii;
typedef pair<double, int> pdi;
typedef pair<LL, LL> pll;
typedef pair<double, double> pdd;
typedef map<int, int> mii;
typedef map<char, int> mci;
typedef map<string, int> msi;
template<class T>
void read(T &res) {
  int f = 1; res = 0;
  char c = getchar();
  while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
  while(c >= '0' && c <= '9') { res = res * 10 + c - '0'; c = getchar(); }
  res *= f;
}
const int ne[8][2] = {1, 0, -1, 0, 0, 1, 0, -1, -1, -1, -1, 1, 1, -1, 1, 1};
const int INF = 0x3f3f3f3f;
const int N = 110;
const LL Mod = 1e9+7;
const int M = 1e5+10;
int a[N], dp[M];
int main() {
  int n; scanf("%d", &n);
  int Max = 0;
  _rep(1, n, i) scanf("%d", &a[i]), Max += a[i];
  dp[0] = 1;
  _rep(1, n, i) {
    for(int j = Max; j >= a[i]; --j) dp[j] = max(dp[j], dp[j-a[i]]);
  }
  _rep(1, n, i) {
    _rep(0, Max-a[i], j) dp[j] = max(dp[j], dp[j+a[i]]);
  }
  int res = 0;
  _rep(1, Max, i) if(dp[i]) ++res;
  printf("%d\n", res);
  return 0;
}

试题 H:杨辉三角形(程序设计)

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

做法:放上一张长一点的杨辉三角图(比赛时可以打表打出来)。
在这里插入图片描述
通过这张图可以看出来:
1、只要行数足够大,第二左斜列可以包含所有的数。
2、中间的数涨的非常快。
可以大致算一下C(3000, 3)>1e9,那么在第3000行以后有贡献的值就只有C(n,2)了。所以对于前3000行,用c数组存下来所有数(这里小心爆精度,所以一旦大于了1e9,把c的值赋为1e9+1,这样的话后面由它推到的数都可被认为无意义的数)。

代码

#include<bits/stdc++.h>
using namespace std;
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define _for(n,m,i) for (int i = (n); i < (m); ++i)
#define _rep(n,m,i) for (int i = (n); i <= (m); ++i)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define PI acos(-1)
#define eps 1e-8
#define rint int
typedef long long LL;
typedef pair<LL, int> pli;
typedef pair<int, int> pii;
typedef pair<double, int> pdi;
typedef pair<LL, LL> pll;
typedef pair<double, double> pdd;
typedef map<int, int> mii;
typedef map<char, int> mci;
typedef map<string, int> msi;
template<class T>
void read(T &res) {
  int f = 1; res = 0;
  char c = getchar();
  while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
  while(c >= '0' && c <= '9') { res = res * 10 + c - '0'; c = getchar(); }
  res *= f;
}
const int ne[8][2] = {1, 0, -1, 0, 0, 1, 0, -1, -1, -1, -1, 1, 1, -1, 1, 1};
const int INF = 0x3f3f3f3f;
const int N = 3010;
const LL Mod = 1e9+7;
const int M = 1e6+10;
LL c[N][N];
map<LL, int> mp;
int main() {
  c[0][1] = 1; mp[1] = 1;
  _rep(1, 3000, i) _rep(0, i, j) {
    if(c[i-1][j] + c[i-1][j+1] <= 1e9) {
      c[i][j+1] = c[i-1][j] + c[i-1][j+1];
      if(!mp[c[i][j+1]]) mp[c[i][j+1]] = (1+i)*i/2+j+1;
    } else c[i][j+1] = 1e9 + 1;
  }
  int n; scanf("%d", &n);
  if(mp[n]) printf("%d\n", mp[n]);
  else printf("%lld\n", 1ll*(1+n)*n/2+2);
  return 0;
}

试题 I:双向排序(程序设计)

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

做法:在大佬的帮助下,终于AC了!
做这种思维题的时候我们要先观察一下性质:

  1. 我们先假设两次操作为 [ 0 , x ] [0,x] [0,x] [ 0 , y ] [0,y] [0,y], 当 x < y x < y x<y 我们发现第一次操作会无效, 当 x > y x > y x>y 第二次操作其实也是无效的,也就是对于连续的同类型操作可以只保留一个,降序保留最大的,升序保留最小的,也就等价于保留使区间变的最大的,其结果不变。
  2. 由于刚开始的时候数组是升序的,所以我们可以认为刚开始有一个操作 [ 1 , 1 ] [1,1] [1,1],再由性质1可以得出最后必定是 1 , 0 1,0 1,0操作交替出现的。我们再来考虑相邻的相同操作(指的是对于10交替出现的次序中的相邻操作),这里就用0操作来说明。对于两个相邻的0操作, [ 0 , x ] [0,x] [0,x] [ 1 , z ] [1,z] [1,z] [ 0 , y ] [0,y] [0,y],当 x < y x < y x<y 时,我们发现 [ 0 , x ] [0,x] [0,x]操作是无效的,因为它会被 [ 0 , y ] [0,y] [0,y]覆盖掉,当 [ 1 , z ] , [ 0 , y ] [1,z],[0,y] [1,z],[0,y]没有交集时, [ 0 , y ] [0,y] [0,y]操作也是无效的,但是不影响结果。也就是说最终相邻相同操作肯定是区间减少的,也就是1操作是递增的,0操作是递减的。相邻不同操作间肯定是有交集的。
  3. 假设我们利用上面的性质处理出来了最终的有效操作序列,那我们该要怎么求结果呢,这一步其实更简单一点,我们发现相邻两个操作的交集就是可能还会再被改变的地方,那么它的补集就是不会变的地方。不变的地方肯定是有序的,根据它最后是被1操作影响还是0操作影响来填进数组里面。

我们来举例说明。假设数组长度是9,最终的有效操作是 [ 1 , 1 ] [ 0 , 7 ] , [ 1 , 5 ] , [ 0 , 6 ] [1,1] [0,7],[1,5],[0,6] [1,1][0,7],[1,5],[0,6]

[ 0 , 7 ] [0,7] [0,7] [ 1 , 1 ] [1,1] [1,1]的交集是 ( 1 , 7 ) (1,7) (1,7),补集是 ( 8 , 9 ) (8,9) (8,9)
7 6 5 4 3 2 1 | 8 9

[ 1 , 5 ] [1,5] [1,5] [ 0 , 7 ] [0,7] [0,7]的交集是 ( 1 , 4 ) (1,4) (1,4),补集是 ( 7 , 5 ) (7,5) (7,5)
7 6 5 | 1 2 3 4

[ 0 , 6 ] [0,6] [0,6] [ 1 , 5 ] [1,5] [1,5]的交集是 ( 3 , 1 ) (3,1) (3,1),补集是 ( 4 , 4 ) (4,4) (4,4)
3 2 1 | 4
最终的结果是7 6 5 3 2 1 4 8 9

代码

#include<bits/stdc++.h>
using namespace std;
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define _for(n,m,i) for (int i = (n); i < (m); ++i)
#define _rep(n,m,i) for (int i = (n); i <= (m); ++i)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define PI acos(-1)
#define eps 1e-8
#define rint int
typedef long long LL;
typedef pair<LL, int> pli;
typedef pair<int, int> pii;
typedef pair<double, int> pdi;
typedef pair<LL, LL> pll;
typedef pair<double, double> pdd;
typedef map<int, int> mii;
typedef map<char, int> mci;
typedef map<string, int> msi;
template<class T>
void read(T &res) {
  int f = 1; res = 0;
  char c = getchar();
  while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
  while(c >= '0' && c <= '9') { res = res * 10 + c - '0'; c = getchar(); }
  res *= f;
}
const int ne[8][2] = {1, 0, -1, 0, 0, 1, 0, -1, -1, -1, -1, 1, 1, -1, 1, 1};
const int INF = 0x3f3f3f3f;
const int N = 1e5+10;
const LL Mod = 1e9+7;
const int M = 1e6+10;
struct xx {
  int op, a;
}xl[N];
int st[N], tp;
int re[N];
int main() {
  int n, m;
  scanf("%d%d", &n, &m);
  _rep(1, m, i) scanf("%d%d", &xl[i].op, &xl[i].a);
  xl[0].op = 1; xl[0].a = 1; st[tp] = 0;
  _rep(1, m, i) {
    if(xl[st[tp]].op) {
      if(xl[i].op) {
        if(xl[i].a < xl[st[tp]].a) tp--;
        else continue;
      } 
    } else {
      if(!xl[i].op) {
        if(xl[i].a > xl[st[tp]].a) tp--;
        else continue;
      } 
    }
    if(xl[i].op) {
      while(tp >= 1 && xl[st[tp-1]].a >= xl[i].a) tp -= 2;
    } else {
      while(tp >= 1 && xl[st[tp-1]].a <= xl[i].a) tp -= 2;
    }
    st[++tp] = i;
  }
  int l = 1, r = n, ll, rr, cnt = n, wll = 0;
  _rep(1, tp, i) {
    //找上下两个操作中序列中需要改变的部分
    if((i&1) && (xl[st[i-1]].a < xl[st[i]].a)) {
      rr = r; r = xl[st[i]].a;
      for(int i = rr; i > r; --i) re[i] = cnt--; 
      wll ^= 1;
    }
    else if(!(i&1) && (xl[st[i-1]].a > xl[st[i]].a)) {
      ll = l; l = xl[st[i]].a;
      for(int i = ll; i < l; ++i) re[i] = cnt--; 
      wll ^= 1;
    }
    else break;
  }
  if(!wll) {
    for(int i = r; i >= l; --i) if(!re[i]) re[i] = cnt--;
  } else {
    for(int i = l; i <= r; ++i) if(!re[i]) re[i] = cnt--;
  }
 //cout << "----------------" << endl;
  _rep(1, n, i) printf("%d%c", re[i], i==n ? '\n' : ' ');
  return 0;
}
/*
3 3
0 3
1 2
0 2
*/

试题 J:括号序列(程序设计)

题意
在这里插入图片描述

做法

代码


  • 13
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值