2023 睿抗机器人开发者大赛CAIP-编程技能赛-本科组(省赛)题解

明天要打睿抗了,于是找来去年省赛题目练练手,题目难度不大,但是和天梯赛一样让人写的头疼,毕竟都是一伙出题人。前一阵子忙期末考和实习,有一阵子没写题了,顺便来网瘾一下qwq。

RC-u1 亚运奖牌榜

思路:记录一下两个国家所获奖牌数,然后根据规则按金银铜的顺序判断一下哪个国家的奖牌数量更多就好了。

时间复杂度: O ( N l o g N ) O(NlogN) O(NlogN)

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int N;
    std::cin >> N;

    std::vector<std::array<int, 3>> a(2);
    for (int i = 0; i < N; i++) {
        int C, P;
        std::cin >> C >> P;

        a[C][P - 1]++;
    }


    for (int i = 0; i < 2; i++) {
        std::cout << a[i][0] << " " << a[i][1] << " " << a[i][2] << "\n";
    }

    auto b = a;
    std::sort(a.begin(), a.end(), std::greater());

    if (a == b) {
        std::cout << "The first win!\n";
    } else {
        std::cout << "The second win!\n";
    }
    return 0;
}

Rc-u2 出院

思路:记录一下题目已经给定的字符串等级映射,然后对于每一次询问首先查询完整字符串是否已经存在,否则枚举字符串的每一个断点查询两个拆开的字符串分别是什么等级进行等级的拼接,若存在多个答案那么标记为D

时间复杂度: O ( N 2 M l o g N ) O(N^{2}MlogN) O(N2MlogN)

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int N, M;
    std::cin >> N >> M;

    std::map<std::string, std::string> f;
    for (int i = 0; i < N; i++) {
        std::string name, level;
        std::cin >> name >> level;

        f[name] = level;
    }

    for (int i = 0; i < M; i++) {
        std::string s;
        std::cin >> s;

        if (f.count(s)) {
            std::cout << f[s] << "\n";
            continue;
        }
        int n = s.size();
        std::vector<std::string> ans;
        for (int i = 0; i < n; i++) {
            std::string l = s.substr(0, i + 1);
            std::string r = s.substr(i + 1);

            if (f.count(l) && f.count(r)) {
                ans.push_back(f[l] + f[r]);
            }
        }

        if (ans.size() != 1) {
            std::cout << "D\n";
        } else {
            std::cout << ans.back() << "\n";
        }
    }
    return 0;
}

Rc-u3 骰子游戏

思路:首先 O ( 6 5 ) O(6^5) O(65)处理出各种排列的等级,这里有一个我比较喜欢用的trick是给当前正在判断等级的序列排序,然后再依据题目规定判断等级,这样能够极大程度地减小处理序列的复杂度,读者可以自行体会。

接下来处理查询中的序列,我们枚举所有可能重置骰子的方案,这里我使用了二进制枚举,那么接下来的事情就是就是开始对于每一种枚举方案搜索有多少种获得更大等级的可能性了。

时间复杂度: O ( 2 5 × 6 5 ) O(2^5 \times 6^5) O(25×65)

#include <bits/stdc++.h>

using i64 = long long;

std::map<std::array<int, 5>, int> f;
void solve() {
    std::array<int, 5> a = {};
    for (int i = 0; i < 5; i++) {
        std::cin >> a[i];
    }

    auto dfs = [&](auto && self, int x, const auto &ok, std::array<int, 5> t, int level) -> int {
        if (x == 5) {
            std::sort(t.begin(), t.end());
            // debug(t);
            if (f[t] > level) {
                // std::cerr << f[t] << " " << level << "\n";
                return 1;
            }

            return 0;
        }

        int ans = 0;
        if (ok[x]) {
            for (int i = 1; i < 7; i++) {
                t[x] = i;
                ans += self(self, x + 1, ok, t, level);
            }
        } else {
            ans += self(self, x + 1, ok, t, level);
        }

        return ans;
    };

    std::sort(a.begin(), a.end());
    std::vector<std::array<int, 3>> info;
    for (int s = 0; s < (1 << 5); s++) {
        std::vector<short> ok(5);
        int ans = 1;
        int cnt = 0;
        for (int i = 0; i < 5; i++) {
            // std::cout << (s >> i & 1);
            if (s >> i & 1) {
                ok[i] = true;
                ans *= 6;
                cnt++;
            }
        }
        // std::cout << "\n";

        // std::cerr << f[a] << "\n";
        int fen = dfs(dfs, 0, ok, a, f[a]);
        int den = ans;

        int g = std::gcd(fen, den);
        fen /= g;
        den /= g;

        // std::cerr << fen << " " << den << "\n";
        info.push_back({fen, den, cnt});
    }

    std::sort(info.begin(), info.end(),
        [&](auto lhs, auto rhs) {
            int l = lhs[0] * rhs[1];
            int r = lhs[1] * rhs[0];

            if (l != r) {
                return l > r;
            } else {
                return lhs[2] < rhs[2];
            }
        });

    auto [fen, den, cnt] = info.front();
    std::cout << cnt << " " << fen << " " << den << "\n";
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);


    for (int a = 1; a < 7; a++) {
        for (int b = 1; b < 7; b++) {
            for (int c = 1; c < 7; c++) {
                for (int d = 1; d < 7; d++) {
                    for (int e = 1; e < 7; e++) {
                        std::array<int, 5> t = {a, b, c, d, e};
                        std::sort(t.begin(), t.end());

                        if (t[0] == t[4]) {
                            f[t] = 8;
                        } else if (t[0] == t[3] || t[1] == t[4]) {
                            f[t] = 7;
                        } else if ((t[0] == t[1] && t[2] == t[4]) || (t[0] == t[2] && t[3] == t[4])) {
                            f[t] = 6;
                        } else if (t == std::array<int, 5>{2, 3, 4, 5, 6}) {
                            f[t] = 5;
                        } else if (t == std::array<int, 5>{1, 2, 3, 4, 5}) {
                            f[t] = 4;
                        } else if (t[0] == t[2] || t[1] == t[3] || t[2] == t[4]) {
                            f[t] = 3;
                        } else if ((t[0] == t[1] && (t[2] == t[3] || t[3] == t[4])) || (t[1] == t[2] && t[3] == t[4])) {
                            f[t] = 2;
                        } else if (t[0] == t[1] || t[1] == t[2] || t[2] == t[3] || t[3] == t[4]) {
                            f[t] = 1;
                        } else {
                            f[t] = 0;
                        }
                    }
                }
            }
        }
    }
    int t;
    std::cin >> t;

    while (t--) {
        solve();
    }
    return 0;
}

Rc-u4 相对论大师

思路:首先,显然每一条推理实际上都是连一条边,这道题目我们一定是需要将字符串映射为编号进行建图的。然后因为每一件事物都有正反两面,所以我们不妨把一件事物的两面编号放在一起,比如0 12 3分别代表事物AB的两面,这样做的好处就是x ^ 1就是x的另一面,便于我们查询是否推理出了起点相反的结论。剩下直接 d f s dfs dfs就好了,过程不再赘述。

时间复杂度: O ( N 2 l o g N ) O(N^{2}logN) O(N2logN)

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int N;
    std::cin >> N;

    std::map<std::string, int> f;
    std::map<int, std::string> g;
    int cur = 0;

    std::vector<std::vector<int>> adj(N * 4);
    std::vector<std::string> sa(N), sb(N);
    for (int i = 0; i < N; i++) {
        std::string A, B;
        int a, b;
        std::cin >> A >> a >> B >> b;

        sa[i] = A + " " + std::to_string(a);
        sb[i] = B + " " + std::to_string(b);
        if (!f.count(sa[i])) {
            f[A + " 0"] = cur++;
            g[cur - 1] = A + " 0";
            f[A + " 1"] = cur++;
            g[cur - 1] = A + " 1";
        } 
        if (!f.count(sb[i])) {
            f[B + " 0"] = cur++;
            g[cur - 1] = B + " 0";
            f[B + " 1"] = cur++;
            g[cur - 1] = B + " 1";
        }

        int u = f[sa[i]];
        int v = f[sb[i]];

        adj[u].push_back(v); 
    }

    // debug(f);

    std::vector<std::vector<std::pair<int, int>>> ans;
    auto dfs = [&](auto &&self, int x, int start, std::vector<std::pair<int, int>> path) -> void {
        // debug(x);
        for (auto y : adj[x]) {
            path.push_back({x, y});
            if (y == start) {
                ans.push_back(path);
                return;
            }

            self(self, y, start, path);
            path.pop_back();
        }
    };
    for (int i = 0; i < N; i++) {
        int x = f[sa[i]];

        dfs(dfs, x, x ^ 1, std::vector<std::pair<int, int>>{});
    }

    std::sort(ans.begin(), ans.end(),
        [&](auto lhs, auto rhs) {
            return lhs.size() < rhs.size();
        });

    auto res = ans.front();
    for (auto [x, y] : res) {
        std::cout << g[x] << " " << g[y] << " ";
    }
    std::cout << "= ";
    std::cout << g[res.front().first] << " " << g[res.back().second] << "\n";
    return 0;
}

Rc-u5 相对成功与相对失败

吐槽:感觉这道题的题面写的不好,两个“必然”很容易引起选手的疑惑,实际上在本题中1 10 0的地位是等价的。

思路:考虑如何把题面的文字语言抽象为数学模型,其实只需要给每一位选手填写问卷的等级记录一个分数就好了,参加睿抗比赛加一分,玩手机游戏减一分,那么此时每一位选手都具备了一个等级。

题目询问最少有几人说谎,实际上就是问最少修改序列的几个位置能使得序列能够满足填写问卷的序列。读者可能会有我将序列匹配搞反的疑惑,实际上无论两个序列谁匹配谁答案都是一致的,不过显然用实际序列去匹配问卷序列更合适一些,因为问卷序列有已经处理好的分数。

正难则反,我们来考虑最多能够保留真实序列的多少个位置,那么就等价于求 在真实序列上有多少个位置在问卷分数上不上升,至此问题转换为求最长不上升子序列的长度,记为len。那么问题的答案就是n - len

时间复杂度: O ( N l o g N ) O(NlogN) O(NlogN)

#include <bits/stdc++.h>

using i64 = long long;

void solve() {
    int N;
    std::cin >> N;

    std::vector<int> score(N);
    for (int i = 0; i < N; i++) {
        int A, B;
        std::cin >> A >> B;
        score[i] += A - B;
    }

    std::vector<int> p(N);
    for (int i = 0; i < N; i++) {
        std::cin >> p[i];
        p[i]--;
    }

    std::multiset<int> s;
    for (int i = N - 1; i >= 0; i--) {
        auto pos = s.upper_bound(score[p[i]]);
        if (pos != s.end()) {
            s.erase(pos);
        }

        s.insert(score[p[i]]);
    }

    int ans = N - s.size();

    std::cout << ans << "\n";
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int t;
    std::cin >> t;

    while (t--) {
        solve();
    }
    return 0;
}
  • 27
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值