十三届蓝桥杯省赛C++ B组
前言
为巩固知识,也为接下来的比赛做准备,故总体进行复习,学习最优解(也可能还有更优的),汲取养分,故记以此博文。
试题 A: 九进制转十进制
1478
试题 B: 顺子日期
#include <iostream>
using namespace std;
const int months[]{ 0,31,28,31,30,31,30,
31,31,30,31,30,31 };
bool check(string str) {
for (int i = 0; i + 2 < str.size(); ++i)
if (str[i + 1] == str[i] + 1 && str[i + 2] == str[i] + 2)
return true;
return false;
}
int main()
{
int year = 2022, month = 1, day = 1;
int res = 0;
for (int i = 0; i < 365; ++i) {
char str[10];
sprintf(str, "%04d%02d%02d", year, month, day);
if (check(str))
++res;
if (++day > months[month]) {
day = 1;
++month;
}
}
cout << res;
return 0;
}
试题 C: 刷题统计
#include <iostream>
using namespace std;
int main()
{
long long a, b, n;
cin >> a >> b >> n;
long long w = 5 * a + 2 * b;
long long res = n / w * 7;
n %= w;
long long d[]{ a,a,a,a,a,b,b };
for (int i = 0; n > 0; ++i) {
n -= d[i];
++res;
}
cout << res << endl;
}
试题 D: 修剪灌木
#include <iostream>
using namespace std;
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
cout << max(i - 1, n - i) * 2 << endl;
}
return 0;
}
试题 E: X 进制减法
#include <iostream>
using namespace std;
#include <algorithm>
long long mod = 1000000007;
const int MAXN = 1E5 + 10;
int arr[MAXN];
int brr[MAXN];
int main()
{
int n, m1, m2;
cin >> n;
cin >> m1;
for (int i = m1 - 1; i >= 0; --i)
cin >> arr[i];
cin >> m2;
for (int i = m2 - 1; i >= 0; --i) {
cin >> brr[i];
}
long long res = 0;
for (int i = m1 - 1; i >= 0; --i) {
res = (res * max({ arr[i] + 1,brr[i] + 1,2 }) + arr[i] - brr[i]) % mod;
}
cout << res % mod;
return 0;
}
试题 F: 统计子矩阵
解法:二维数组前缀和+枚举边界 时间O(N^4) AC 70%,下方有最优解。
如果了较二维数组前缀和,此法较易想到,能拿到70%分数。
#include<iostream>
using namespace std;
const int N = 510;
long long sum[N][N];
long long arr[N][N];
void init(int n, int m) {
for (register int i = 1; i <= n; ++i) {
for (register int j = 1; j <= m; ++j) {
cin >> arr[i][j];
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + arr[i][j];
}
}
}
long long sub(int x1, int y1, int x2, int y2) {
return sum[x1][y1] - sum[x1][y2] - sum[x2][y1] + sum[x2][y2];
}
int main()
{
int n, m, k;
cin >> n >> m >> k;
init(n, m);
long long ans = 0;
for (int xx = 1; xx <= n; ++xx) {
for (int yy = 1; yy <= m; ++yy) {
for (int x = 0; x < n; ++x) {
if (xx <= x)continue;
for (int y = 0; y < m; ++y) {
if (yy <= y)continue;
if (sub(xx, yy, x, y) <= k)
++ans;
}
}
}
}
cout << ans;
return 0;
}
解法:前缀和+枚举左右边界+滑动窗口 时间O(N^3)
#include<iostream>
using namespace std;
const int N = 510;
long long sum[N][N];
long long arr[N][N];
void init(int n, int m) {
for (register int i = 1; i <= n; ++i) {
for (register int j = 1; j <= m; ++j) {
cin >> arr[i][j];
sum[i][j] = sum[i][j - 1] + arr[i][j];
}
}
}
long long sub(int r, int c1, int c2) {
return sum[r][c2] - sum[r][c1];
}
int main()
{
int n, m, k;
cin >> n >> m >> k;
init(n, m);
long long ans = 0;
for (register int l = 0; l < m; ++l) {
for (register int r = l + 1; r <= m; ++r) {
long long total = 0;
int high = 0, low = 0;
for (high = 1; high <= n; ++high) {
total += sub(high, l, r);
while (total > k) {
++low;
total -= sub(low, l, r);
}
ans += high - low ;
}
}
}
cout << ans;
return 0;
}
试题 H: 扫雷
#include <iostream>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 50010, M = 999997;
int n, m;
struct Circle {
int x, y, r;
}cir[N];
LL h[M];
int id[M];
bool st[M];
LL get_key(int x, int y) {
return x * 1000000001LL + y;
}
int find(int x, int y) {
LL key = get_key(x, y);
int t = (key % M + M) % M;
while (h[t] != -1 && h[t] != key) {
if (++t == M)
t = 0;
}
return t;
}
int sqr(int x) {
return x * x;
}
void dfs(int x, int y, int r) {
st[find(x, y)] = true;
for (int i = x - r; i <= x + r; ++i) {
for (int j = y - r; j <= y + r; ++j) {
if (sqr(i - x) + sqr(j - y) <= sqr(r)) {
int t = find(i, j);
if (id[t] && !st[t])
dfs(i, j, cir[id[t]].r);
}
}
}
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
for (int i = 1; i <= n; ++i) {
int x, y, r;
scanf("%d%d%d", &x, &y,&r);
cir[i] = { x,y,r };
int t = find(x, y);
if (h[t] == -1)
h[t] = get_key(x, y);
if (!id[t] || cir[id[t]].r < r)
id[t] = i;
}
while (m--) {
int x, y, r;
scanf("%d%d%d", &x, &y, &r);
for (int i = x - r; i <= x + r; ++i) {
for (int j = y - r; j <= y + r; ++j) {
if (sqr(i - x) + sqr(j - y) <= sqr(r)) {
int t = find(i, j);
if (id[t] && !st[t])
dfs(i, j, cir[id[t]].r);
}
}
}
}
int res = 0;
for (int i = 1; i <= n; ++i) {
if (st[find(cir[i].x, cir[i].y)])
++res;
}
printf("%d", res);
return 0;
}
试题 I: 李白打酒加强版
#include <iostream>
using namespace std;
#include <cstring>
const int N = 110, MOD = 1e9 + 7;
int n, m;
int f[N][N][N];
int main()
{
cin >> n >> m;
f[0][0][2] = 1;
for (int i = 0; i <= n; ++i)
for (int j = 0; j <= m; ++j)
for (int k = 0; k <= m; ++k) {
int& v = f[i][j][k];
if (i && k % 2 == 0)
v = (v + f[i - 1][j][k / 2]) % MOD;
if (j)
v = (v + f[i][j - 1][k + 1]) % MOD;
}
cout << f[n][m - 1][1] << endl;
return 0;
}
试题 J: 砍竹子
优先队列模拟,时间复杂度基本没富裕,O(6NlogN) 6是任何一个高度的竹子不超过6次操作即可变为高度1
#include<iostream>
using namespace std;
#include <cmath>
#include <queue>
typedef long long LL;
const int N = 200010;
int n;
LL h[N];
struct Seg {
int l, r;
LL v;
bool operator<(const Seg& S) const {
if (v != S.v)
return v < S.v;
return l > S.l;
}
};
LL f(LL x)
{
return sqrt(x / 2 + 1);
}
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; ++i) {
scanf("%lld", &h[i]);
}
priority_queue<Seg> heap;
for (int i = 0; i < n; ++i) {
int j = i + 1;
while (j < n && h[i] == h[j])
++j;
heap.push({ i,j - 1,h[i] });
i = j - 1;
}
int res = 0;
while (heap.size() > 1 || heap.top().v > 1) {
auto t = heap.top();
heap.pop();
while (heap.size() && heap.top().v == t.v && t.r + 1 == heap.top().l)
{
t.r = heap.top().r;
heap.pop();
}
heap.push({ t.l,t.r,f(t.v) });
if (t.v > 1)
++res;
}
printf("%d\n", res);
return 0;
}
思维
#include<iostream>
using namespace std;
#include <cmath>
typedef long long LL;
const int N = 200010, M = 10;
int n,m;
int f[N][M];
int main()
{
scanf("%d", &n);
LL stk[M];
int res = 0;
for (int i = 0; i < n; ++i) {
LL x;
int top = 0;
scanf("%lld", &x);
while (x > 1) stk[++top] = x, x = sqrt(x / 2 + 1);
res += top;
m = max(m, top);
for (int j = 0, k = top; k; ++j, --k) {
f[i][j] = stk[k];
}
}
for (int i = 0; i < m; ++i) {
for (int j = 1; j < n; ++j) {
if (f[j][i] && f[j][i] == f[j - 1][i])
--res;
}
}
printf("%d\n", res);
return 0;
}
后言
最后也是记录一下荣获省一的成绩,🤭,但经此发现,自己还有很多不足,还是弱弱一枚,还得精进算法🤤
其中绝大部分来自y总视频讲解,不过自己添加了注释便于理解。讲解视频见此
最后给大家几个网络供学习测试,New Online Judge,C语言网。
|