第十四届蓝桥杯省赛C++B组个人代码(未检验)

2023 年 4 月 8 日是蓝桥杯省赛,今年我参加的是 C++ 组 B 组,虽然说打得不是很理想,不过好在个人感觉省一问题不是很大,反正只要是省一对得多对得少都一样。

比赛中的代码是没法保存的,所以我借着新鲜的记忆,重新把我会写的题的代码都码了一遍,只是过了样例,不能保证是正确答案,不过思路大差不差,应该还是具有借鉴价值的吧。

A. 日期统计 (已通过民间数据)

第十四届蓝桥杯省赛C++B组个人代码(未检验)_第1张图片

235
#include 
const int n = 100;
bool check(std::string &s, std::string &t) {
    int j = 0;
    for (int i = 0; i < n; i++) {
        if (j < (int) t.size() && s[i] == t[j]) {
            j++;
        }
    }
    if (j == t.size())
        return true;
    return false;
};

int main() {
    std::string s = "5686916124919823647759503875815861830379270588570991944686338516346707827689565614010094809128502533";
    int ans = 0;
    std::string year = "2023";
    int d[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    for (int i = 1; i <= 12; i++) {
        std::string month = std::to_string(i);
        if (month.size() == 1) {
            month = "0" + month;
        }
        for (int j = 1; j <= d[i]; j++) {
            std::string day = std::to_string(j);
            if (day.size() == 1) {
                day = "0" + day;
            }
            std::string t = year + month + day;
            if (check(s, t)) {
                ans++;
            }
        }
    }
    std::cout << ans;
    
    return 0;
}

B. 01串的熵 (已通过民间数据)

第十四届蓝桥杯省赛C++B组个人代码(未检验)_第2张图片

11027421
#include "bits/stdc++.h"
double eps = 1E-4;
double get(int x, int y)
{
    double zero = 1.0 * x / (1.0 * (x + y));
    double one = 1.0 * y / (1.0 * (x + y));
    return -(x * zero * log2(zero) + y * one * log2(one));
}

int main() {
    int n = 23333333;
    for (int i = 0; i <= n; i++) {
        if (abs(get(i, n - i) - 11625907.5798) <= eps) {
            std::cout << i << '\n';
            return 0;
        }
    }
    return 0;
}

C. 冶炼金属 (已通过民间数据)

第十四届蓝桥杯省赛C++B组个人代码(未检验)_第3张图片

第十四届蓝桥杯省赛C++B组个人代码(未检验)_第4张图片

第十四届蓝桥杯省赛C++B组个人代码(未检验)_第5张图片

#include 
#include 
#define int long long
const int N = 1e4 + 1;
std::pair a[N];
signed main()
{
    int n;
    std::cin >> n;
    int mx = INT_MAX;
    for (int i = 0; i < n; i ++) {
        std::cin >> a[i].first >> a[i].second;
        mx = std::min(mx, a[i].first / a[i].second);
    }
    int l = 1, r = 2e9, mn = r;
    while(l <= r) {
        int mid = (l + r) >> 1;
        bool ok = 1;
        for (int i = 0; i < n; i ++) {
            if(a[i].first / mid > a[i].second) {
                ok = 0;
                break;
            }
        }
        if(ok) {
            mn = mid;
            r = mid - 1;
        }
        else l = mid + 1;
    }
    std::cout << mn << ' ' << mx;
    
    return 0;
}

对于这种签到题,二分未免有点过于小题大做了,直接贪心即可

#include 
#include 
#define int long long
signed main()
{
    int n;
    std::cin >> n;
    int mx = INT_MAX, mn = 0;
    for (int i = 0; i < n; i ++) {
        int a, b;
        std::cin >> a >> b;
        mx = std::min(mx, a / b);
        mn = std::max(mn, a / (b + 1) + 1);
    }
    std::cout << mn << ' ' << mx;
    return 0;
}

D. 飞机降落 (已通过民间数据)

第十四届蓝桥杯省赛C++B组个人代码(未检验)_第6张图片

 第十四届蓝桥杯省赛C++B组个人代码(未检验)_第7张图片

 听说这题可以用状压DP,不过我这种菜鸡显然只会全排列无脑暴力。毕竟复杂度也在允许范围内嘛~

复杂度 O(n\times n!) .

#include 
#include 
struct node {
    int l, r, len;
};
node a[11];
int idx[11]; //存放下标的数组,便于全排列
void solve()
{
    int n;
    std::cin >> n;
    for (int i = 1; i <= n; i ++) {
        std::cin >> a[i].l >> a[i].r >> a[i].len;
        a[i].r += a[i].l;
    }
    for (int i = 1; i <= n; i ++)
        idx[i] = i;
    do {
        int rs = a[idx[1]].l + a[idx[1]].len;
        bool ok = 1;
        for (int k = 2; k <= n; k ++) {
            int i = idx[k];
            if(a[i].r < rs) {
                ok = 0;
                break;
            }
            rs = std::max(rs, a[i].l) + a[i].len;
        }
        if(ok) {
            std::cout << "YES\n";
            return;
        }
    }while(std::next_permutation(idx + 1, idx + n + 1));
    std::cout << "NO\n";
}

signed main()
{
    int t = 1;
    std::cin >> t;
    while(t --) {
        solve();
    }
    
    return 0;
}

E. 接龙数列 (已通过民间数据)

第十四届蓝桥杯省赛C++B组个人代码(未检验)_第8张图片

 第十四届蓝桥杯省赛C++B组个人代码(未检验)_第9张图片

 这题很明显是需要DP的,正常的写法是复杂度 \small O(n^2) ,可以过前 50% 的数据点。

#include 
#include 
#include 
const int N = 1e5 + 1;
int dp[N];

signed main()
{
    int n;
    std::cin >> n;
    std::vector a(n + 1);
    for (int i = 1; i <= n; i ++) {
        std::cin >> a[i];
        dp[i] = 1;
        for (int j = 1; j < i; j ++) {
            if(a[i].front() == a[j].back()) {
                dp[i] = std::max(dp[i], dp[j] + 1);
            }
        }
    }
    int ans = 0;
    for (int i = 1; i <= n; i ++) {
        ans = std::max(ans, dp[i]);
    }
    std::cout << n - ans;

    return 0;
}

我们可以将其优化至 O(n) 复杂度,就可以过 100% 的数据点了

#include 
#include 
#include 
const int N = 1e5 + 1;
int dp[N];
int pre[11];
signed main()
{
    memset(pre, -1, sizeof(pre));
    int n;
    std::cin >> n;
    std::vector a(n + 1);
    for (int i = 1; i <= n; i ++) {
        std::cin >> a[i];
    }
    int ans = 0;
    for (int i = 1; i <= n; i ++) {
        int x = a[i].front() - '0';
        int y = a[i].back() - '0';
        if (pre[x] != -1)
            dp[i] = dp[pre[x]] + 1;
        else
            dp[i] = 1;
        ans = std::max(ans, dp[i]);
        if (pre[y] == -1)
            pre[y] = i;
        else if(dp[i] > dp[pre[y]])
            pre[y] = i;
    }
    std::cout << n - ans;
 
    return 0;
}

F. 岛屿个数 (已通过民间数据)

第十四届蓝桥杯省赛C++B组个人代码(未检验)_第10张图片

 第十四届蓝桥杯省赛C++B组个人代码(未检验)_第11张图片

 第十四届蓝桥杯省赛C++B组个人代码(未检验)_第12张图片

 第十四届蓝桥杯省赛C++B组个人代码(未检验)_第13张图片

在最外面加一圈 0 ,然后从左上角开始往 8 个方向搜索0,形成的空洞,搜索空洞数量即可。

复杂度 O(T\times n^2) .

#include 
#include 
#include 
#include 

const int N = 55, black = -1; //用-1代表染黑的地方
int g[N][N], g2[N][N], st[N][N];
int dx[] = {1, 0, -1, 0, 1, 1, -1, -1}, dy[] = {0, 1, 0, -1, 1, -1, 1, -1};
int n, m;
int cur; //记录某个岛屿的编号

void color(int I, int J)
{
    std::queue > q;
    q.push({I, J});
    st[I][J] = 1;
    g2[I][J] = black;
    while(q.size()) {
        int x = q.front().first, y = q.front().second;
        q.pop();
        for (int i = 0; i < 8; i ++) {
            int xx = x + dx[i], yy = y + dy[i];
            if(0 <= xx && xx <= n + 1 && 0 <= yy && yy <= m + 1 && 
                    g[xx][yy] == 0 && st[xx][yy] == 0) {
                st[xx][yy] = 1;
                g2[xx][yy] = black;
                q.push({xx, yy});
            }
        }
    }
}

void bfs(int I, int J)
{
    std::queue > q;
    q.push({I, J});
    st[I][J] = 1;
    g2[I][J] = cur;
    while(q.size()) {
        int x = q.front().first, y = q.front().second;
        q.pop();
        for (int i = 0; i < 4; i ++) {
            int xx = x + dx[i], yy = y + dy[i];
            if(0 <= xx && xx <= n + 1 && 0 <= yy && yy <= m + 1 && 
                    g2[xx][yy] == 0 && st[xx][yy] == 0) {
                st[xx][yy] = 1;
                g2[xx][yy] = cur;
                q.push({xx, yy});
            }
        }
    }
}

void solve()
{
    memset(g, 0, sizeof(g));
    memset(g2, 0, sizeof(g2));
    memset(st, 0, sizeof(st));
    std::cin >> n >> m;
    for (int i = 1; i <= n; i ++) {
        for (int j = 1; j <= m; j ++) {
            char x;
            std::cin >> x;
            g[i][j] = x - '0';
        }
    }
    color(0, 0);
    cur = 0;
    for (int i = 0; i <= n + 1; i ++) {
        for (int j = 0; j <= m + 1; j ++) {
            if(g2[i][j] == 0) {
                cur++;
                bfs(i, j);
            }
        }
    }
    std::cout << cur << '\n';
}

signed main()
{
    int t = 1;
    std::cin >> t;
    while(t --) {
        solve();
    }

    return 0;
}

G. 子串简写 (已通过民间数据)

第十四届蓝桥杯省赛C++B组个人代码(未检验)_第14张图片

 第十四届蓝桥杯省赛C++B组个人代码(未检验)_第15张图片

算是一个简单的签到题,直接存储下标,然后二分就行了,复杂度 \small O(nlogn) 。

#include 
#include 
#include 
#include 
#define int long long
 
signed main()
{
    int k;
    std::cin >> k;
    std::string s;
    std::cin >> s;
    char c1, c2;
    std::cin >> c1 >> c2;
    int n = s.size();
    std::vector a1, a2;
    for (int i = 0; i < n; i ++) {
        if(s[i] == c1)
            a1.push_back(i + 1);
        if(s[i] == c2)
            a2.push_back(i + 1);
    }
    int ans = 0;
    for (size_t i = 0; i < a1.size(); i ++) {
        if(a2.size() == 0)
            continue;
        int loc = std::lower_bound(a2.begin(), a2.end(), a1[i] + k - 1) - a2.begin();
        ans += (int)a2.size() - loc;
    }
    std::cout << ans;
 
    return 0;
}

H. 整数删除 (已通过民间数据)

第十四届蓝桥杯省赛C++B组个人代码(未检验)_第16张图片

 第十四届蓝桥杯省赛C++B组个人代码(未检验)_第17张图片

这题比赛中只写出个暴力,也就是说只能过掉前 20% 的数据。不过这题其实是会写的,只不过赛中时间不是很多,就先打了个暴力做后面的去了(

O(n^2) 解法:

#include 
#include 
#include 
const int N = 5e5 + 1;
int a[N];

signed main()
{
    int n, k;
    std::cin >> n >> k;
    for (int i = 1; i <= n; i ++) {
        std::cin >> a[i];
    }
    for (int i = 1; i <= k; i ++) {
        int loc = std::min_element(a + 1, a + n + 1) - a;
        a[loc - 1] += a[loc];
        a[loc + 1] += a[loc];
        for (int j = loc; j < n; j ++) {
            a[j] = a[j + 1];
        }
        --n;
    }
    for (int i = 1; i <= n; i ++)
        std::cout << a[i] << ' ';

    return 0;
}

优先队列解法 O(nlogn) :

#include "bits/stdc++.h"

using ll = long long;
std::priority_queue, 
    std::vector>, std::greater>> q;

int main() {

    int n, k;
    std::cin >> n >> k;
    std::vector a(n);
    for (int i = 0; i < n; i++) {
        std::cin >> a[i];
    }

    for (int i = 0; i < n; i++) {
        q.push({a[i], i});
    }

    std::vector pre(n), nex(n);
    std::iota(pre.begin(), pre.end(), -1);
    std::iota(nex.begin(), nex.end(), 1);

    while (q.size()) {
        auto Top = q.top();
        ll ai = Top.first;
        int i = Top.second;
        q.pop();
        if (ai != a[i]) {
            q.push({a[i], i});
            continue;
        }
        a[i] = -1;
        int Nex = nex[i];
        int Pre = pre[i];
        if (Nex < n) {
            a[Nex] += ai;
            pre[Nex] = Pre;
        }
        if (Pre >= 0) {
            a[Pre] += ai;
            nex[Pre] = Nex;
        }
        k--;
        if (k == 0) {
            break;
        }
    }

    for (int i = 0; i < n; i++) {
        if(a[i] != -1)
            std::cout << a[i] << ' ';
    }

    return 0;
}

I. 景区导游 (已通过民间数据)

第十四届蓝桥杯省赛C++B组个人代码(未检验)_第18张图片第十四届蓝桥杯省赛C++B组个人代码(未检验)_第19张图片

 这题看似很难,其实是比较简单的算法题,需要用到 LCA 算法,比赛中我码了 40 多分钟,不过好在过了样例,没有出现 bug ,也是让我舒了一口气。

复杂度 O(n logn) .

#include 
#include 
#include 
#define int long long
int n, k;
const int N = 1e5 + 5, M = 2e5 + 5;
int f[N][20], d[N][20];
int a[N], dep[N];
int d1[N], d2[N];
int h[N], ne[M], e[M], w[M], idx;
void add(int a, int b, int c)
{
    e[idx] = b; w[idx] = c; ne[idx] = h[a]; h[a] = idx++;
}

void dfs(int cur, int pre, int dis)
{
    dep[cur] = dep[pre] + 1;
    f[cur][0] = pre;
    d[cur][0] = dis;
    for (int i = 1; i <= std::__lg(dep[cur]); i++) {
        f[cur][i] = f[f[cur][i - 1]][i - 1];
        d[cur][i] = d[cur][i - 1] + d[f[cur][i - 1]][i - 1];
    }
    for (int i = h[cur]; i != -1; i = ne[i]) {
        int x = e[i];
        if (x == pre)
            continue;
        dfs(x, cur, w[i]);
    }
}

int lca(int u, int v)
{
    if (dep[u] < dep[v]) {
        std::swap(u, v);
    }
    int ans = 0;
    for (int i = (int) std::__lg(dep[u] - dep[v]); i >= 0; i--) {
        if (dep[f[u][i]] >= dep[v]) {
            ans += d[u][i];
            u = f[u][i];
        }
    }
    if (u == v)
        return ans;
    for (int i = (int) std::__lg(dep[u]); i >= 0; i--) {
        if (f[u][i] !=  f[v][i]) {
            ans += d[u][i];
            ans += d[v][i];
            u = f[u][i];
            v = f[v][i];
        }
    }
    return ans + d[u][0] + d[v][0];
}

signed main()
{
    memset(h, -1, sizeof(h));
    std::cin >> n >> k;
    for (int i = 1; i < n; i ++) {
        int u, v, w;
        std::cin >> u >> v >> w;
        add(u, v, w);
        add(v, u, w);
    }
    for (int i = 1; i <= k; i ++)
        std::cin >> a[i];
    dfs(1, 0, 0);
    int p1 = 0, p2 = 0;
    for (int i = 2; i <= k; i ++) {
        d1[p1++] = lca(a[i], a[i - 1]);
        if(i != 2)
            d2[p2++] = lca(a[i], a[i - 2]);
    }
    int sum = 0;
    for (int i = 0; i < p1; i++)
        sum += d1[i];
    for (int i = 1; i <= k; i ++) {
        int ans = 0;
        if(i == 1)
            ans = sum - d1[i - 1];
        else if(i == k)
            ans = sum - d1[i - 2];
        else ans = sum - d1[i - 1] - d1[i - 2];
        if(i != 1 && i != k) {
            ans += d2[i - 2];
        }
        std::cout << ans << ' ';
    }
    return 0;
}

J. 砍树 (已通过民间数据)

第十四届蓝桥杯省赛C++B组个人代码(未检验)_第20张图片

 第十四届蓝桥杯省赛C++B组个人代码(未检验)_第21张图片

 这一题赛中已经没时间了(当时只剩20分钟了),所以就写了个 \small O(n^2) 的暴力(其实哪怕有时间也估计做不出来了)。

#include 
#include 
const int N = 1e5 + 1;
int f[N];
std::pair a[N], b[N];
int find(int x)
{
    if(x != f[x])
        f[x] = find(f[x]);
    return f[x];
}

signed main()
{
    int n, m;
    std::cin >> n >> m;
    
    for (int i = 1; i < n; i++) {
        int x, y;
        std::cin >> x >> y;
        a[i] = {x, y};
    }
    for (int i = 1; i <= m; i ++) {
        int x, y;
        std::cin >> x >> y;
        b[i] = {x, y};
    }

    for (int i = n - 1; i >= 1; i --) {
        for (int j = 1; j <= n; j ++)
            f[j] = j;
        for (int j = 1; j < n; j ++) {
            if(i == j)
                continue;
            int l = a[j].first, r = a[j].second;
            l = find(l), r = find(r);
            if(l != r)
                f[l] = r;
        }
        bool ok = 1;
        for (int j = 1; j <= m; j ++) {
            int l = b[j].first, r = b[j].second;
            l = find(l), r = find(r);
            if(l == r) {
                ok = 0;
                break;
            }
        }
        if(ok) {
            std::cout << i << '\n';
            return 0;
        }
    }
    std::cout << -1;

    return 0;
}

正解的话可以用 LCA + 树上差分,处理出每条树上边被 \small (a_i,b_i) 路径经过的次数,最大的被经过 m 次的边就是答案。

复杂度 O(nlogn) .

#include 
#include 
#include 
#include 
//#define int long long
int n, m;
const int N = 1e5 + 5, M = 2e5 + 5;
int f[N][20];
int dep[N], d[N];
int h[N], ne[M], e[M], idx;
void add(int a, int b)
{
    e[idx] = b; ne[idx] = h[a]; h[a] = idx++;
}
 
void dfs(int cur, int pre)
{
    dep[cur] = dep[pre] + 1;
    f[cur][0] = pre;
    for (int i = 1; i <= std::__lg(dep[cur]); i++) {
        f[cur][i] = f[f[cur][i - 1]][i - 1];
    }
    for (int i = h[cur]; i != -1; i = ne[i]) {
        int next = e[i];
        if (next == pre)
            continue;
        dfs(next, cur);
    }
}
 
int lca(int u, int v)
{
    if (dep[u] < dep[v])
        std::swap(u, v);
    for (int i = (int) std::__lg(dep[u] - dep[v]); i >= 0; i--) {
        if (dep[f[u][i]] >= dep[v]) {
            u = f[u][i];
        }
    }
    if (u == v)
        return u;
    for (int i = (int) std::__lg(dep[u]); i >= 0; i--) {
        if (f[u][i] !=  f[v][i]) {
            u = f[u][i];
            v = f[v][i];
        }
    }
    return f[u][0];
}

void dfs2(int cur, int pre) {
    for (int i = h[cur]; i != -1; i = ne[i]) {
        int next = e[i];
        if (next != pre) {
            dfs2(next, cur);
            d[cur] += d[next];
        }
    }
}
 
signed main()
{
    memset(h, -1, sizeof(h));
    std::cin >> n >> m;
    std::vector > edge(n);
    for (int i = 1; i < n; i ++) {
        std::cin >> edge[i].first >> edge[i].second;
        edge[i] = {edge[i].first, edge[i].second};
        add(edge[i].first, edge[i].second);
        add(edge[i].second, edge[i].first);
    }
    dfs(1, 0);
    for (int i = 1; i <= m; i ++) {
        int a, b;
        std::cin >> a >> b;
        int fa = lca(a, b);
        d[a]++, d[b]++;
        d[fa]--;
        d[f[fa][0]]--;
    }
    dfs2(1, 0);
    for (int i = n - 1; i >= 1; i --) {
        if(d[edge[i].first] == m && d[edge[i].second] == m) {
            std::cout << i;
            return 0;
        }
    }
    std::cout << -1;

    return 0;
}

日后有时间更新正解~

你可能感兴趣的:(蓝桥杯,c++,算法)