2025年睿抗机器人开发者大赛CAIP-编程技能赛-本科组(国赛)解题报告 | 珂学家

2025年睿抗机器人开发者大赛CAIP-编程技能赛-本科组(国赛)解题报告 | 珂学家

前言

在这里插入图片描述

题解

2025年睿抗机器人开发者大赛CAIP-编程技能赛-本科组(国赛)解题报告

睿抗一如既往的码量大,喜欢阅读理解挖坑,T_T。

T3 应该是最简单,如果去掉匹配串 2 字节的限制,感觉会是一道有趣的题。

在这里插入图片描述

RC-u1 谁拿冠军了?

分值: 15分

考察点:hash表的使用

注意点:明明某一天里,可能存在多个相同操作,需要求其总和,在除 2。

#include<bits/stdc++.h>usingnamespace std;intmain(){int n, m; cin >> n >> m;int A1, A2, B1, B2, B3; cin >> A1 >> A2 >> B1 >> B2 >> B3;// 利用 array<int, 2> 表示二元组 (d,op) map<array<int,2>,int> ops;for(int i =0; i < n; i++){int d, op; cin >> d >> op; ops[{d, op}]+=1;}// 题意保证 一天最多一个操作 map<int,int> ban;for(int i =0; i < m; i++){int t, op; cin >> t >> op; ban[t]= op;}int sw1 =0, sw2 =0;for(auto[k, cnt]: ops){auto[d, op]= k;// 减半影响的操作if(ban.count(d)&& ban[d]== op){if(op ==1){ sw1 += A1 * cnt; sw2 -= B1 * cnt /2;}elseif(op ==2){ sw2 -= B2 * cnt /2;}elseif(op ==3){ sw1 -= A2 * cnt; sw2 -= B3 * cnt /2;}}else{if(op ==1){ sw1 += A1 * cnt; sw2 -= B1 * cnt;}elseif(op ==2){ sw2 -= B2 * cnt;}elseif(op ==3){ sw1 -= A2 * cnt; sw2 -= B3 * cnt;}}} cout << sw1 <<" "<< sw2 <<"\n";return0;}

RC-u2 理包

分值: 20分

思路:模拟

枚举包裹的空坐标,然后以物品坐标系,以相对偏移向量尝试填充匹配

感觉码量有点大,看起来简单,但写起来稍头痛。

#include<bits/stdc++.h>usingnamespace std;intmain(){int n, m, q; cin >> n >> m >> q; vector<string>g(n);for(int i =0; i < n; i++){ g[i].resize(m,'.');}while(q-->0){int r, c; cin >> r >> c; vector<string>mat(r);int sy =-1, sx =-1;for(int i =0; i < r; i++){ cin >> mat[i];if(sy ==-1){for(int j =0; j < c; j++){if(mat[i][j]=='*'){ sy = i; sx = j;break;}}}}// 以物品坐标去匹配包裹,oy/ox是相对偏移向量auto check =[&](int oy,int ox)->bool{for(int i =0; i < r; i++){for(int j =0; j < c; j++){if(mat[i][j]=='*'){if(i + oy >=0&& i + oy < n && j + ox >=0&& j + ox < m){if(g[i + oy][j + ox]=='*'){returnfalse;}}else{returnfalse;}}}}returntrue;};// 以物品坐标去填充包裹,oy/ox是相对偏移向量auto fill =[&](int oy,int ox){for(int i =0; i < r; i++){for(int j =0; j < c; j++){if(mat[i][j]=='*'){ g[i + oy][j + ox]='*';}}}};// 从包裹坐标出发,寻找空格去,查询是否放入物品 auto solve =[&](int&ay,int&ax)->bool{for(int i =0; i < n; i++){for(int j =0; j < m; j++){if(g[i][j]=='.'){if(check(i - sy, j - sx)){ ay = i; ax = j;fill(i - sy, j - sx);returntrue;}}}}returnfalse;};int ay =0, ax =0;if(solve(ay, ax)){ cout <<(ay +1)<<" "<<(ax +1)<<"\n";}else{ cout <<-1<<" "<<-1<<"\n";}}return0;}

RC-u3 删除屏蔽词

分值: 25分

思路:模拟+栈

这题限定模式串固定为2,如果为k,那这题就麻烦了。

就是当前字符,以及栈顶元素,是否构成模式串

注意: 需要输出 删除次数,不是最后文本的长度

#include<bits/stdc++.h>usingnamespace std;intmain(){ string p; string s;getline(cin, p);getline(cin, s);int del =0; stack<char> stk;for(char c: s){// 核心代码,就是这一行if(c == p[1]&&!stk.empty()&& stk.top()== p[0]){ stk.pop(); del++;}else{ stk.push(c);}} string buf;while(!stk.empty()){ buf.push_back(stk.top()); stk.pop();}reverse(buf.begin(), buf.end()); cout << del <<" "<< buf <<"\n";return0;}

RC-u4 穷游

分值: 30分

思路:二分 + dijkstra

可以先确定最小的住宿费用,再求解此基础上的最小时长。

这个套路,在睿抗编程赛中,多次出现。

二分check的核心逻辑是连通性,为了简化代码,可以复用带限制的求时长dijkstra

这样在追求编码效率 和 代码 AC 之间达到一个好的平衡。

#include<bits/stdc++.h>usingnamespace std;structE{int v, w;E(){}E(int v,int w):v(v),w(w){}};structP{int u, c;P(){}P(int u,int c):u(u),c(c){}};intmain(){int n, m; cin >> n >> m; vector<int>prices(n);for(int&x: prices) cin >> x; vector<vector<E>>g(n);for(int i =0; i < m; i++){int u, v, w; cin >> u >> v >> w; u--; v--; g[u].push_back(E(v, w)); g[v].push_back(E(u, w));}int s, e; cin >> s >> e; s--; e--;auto comp =[](const P&lhs,const P&rhs){return lhs.c > rhs.c;};int inf =0x3f3f3f3f;auto dijkstra =[&](int limit){ vector<int>dp(n, inf); dp[s]=0; priority_queue<P, vector<P>,decltype(comp)>pq(comp); pq.push(P(s,0));while(!pq.empty()){ P p = pq.top(); pq.pop();if(p.c > dp[p.u])continue;// 提前返回if(p.u == e){return p.c;}for(E &e: g[p.u]){if(prices[e.v]> limit)continue;if(dp[e.v]> p.c + e.w){ dp[e.v]= p.c + e.w; pq.push(P(e.v, dp[e.v]));}}}return inf;};int l =0, r =*max_element(prices.begin(), prices.end());// 二分确认最小费用while(l <= r){int mid = l +(r - l)/2;int ret =dijkstra(mid);if(ret != inf){ r = mid -1;}else{ l = mid +1;}}// 再求解最小时长int ret =dijkstra(l); cout << l <<" "<< ret <<"\n";return0;}

RC-u5 工序优化

分值: 30

思路: 动态规划

题目可以归纳如下,更好的理解

定义一个merge操作

  • 第i项可以和第i-1项合并,时间合并,工作时间按第i-1项为准,但需额外消耗 CiC_iCi​
  • 合并可以连续,即 [ia, b]的连续区间可以合并,操作次数为(b - a)次
  • 这个操作为最多 k 次

如何理解呢?

定义把[a, b]连续区间进行合并,则

E[a,b]合并的时间消耗=(∑i=ai=bPi)∗Ta,E[a, b]合并的时间消耗=(\sum_{i=a}^{i=b} P_i) * T_a,E[a,b]合并的时间消耗=(i=a∑i=b​Pi​)∗Ta​,
E[a,b]合并的代价=cost[a,b]=∑i=a+1i=bCiE[a, b]合并的代价= cost[a, b]=\sum_{i=a+1}^{i=b} C_i E[a,b]合并的代价=cost[a,b]=i=a+1∑i=b​Ci​

能理解这个核心的概念后,那个这个 dp 就相对容易解了

  1. 定义状态dp[i][j]为前i项,使用j次merge操作的最小时间/代价dp[i][j] 为前 i 项,使用 j 次merge 操作的最小时间/代价dp[i][j]为前i项,使用j次merge操作的最小时间/代价
  2. 转移方程dp[i+1+s][j+s]=min⁡s∈[0,k−j]dp[i][j]+E[i+1,i+1+s]dp[i + 1 + s][j + s] = \min_{s\in[0, k - j]} { dp[i][j] + E[i + 1, i + 1 + s] }dp[i+1+s][j+s]=s∈[0,k−j]min​dp[i][j]+E[i+1,i+1+s]
  3. 结果ans=min⁡j∈[0,k]dp[n−1][j] ans = \min_{j\in[0, k]} { dp[n - 1][j] } ans=j∈[0,k]min​dp[n−1][j]

时间复杂度为O(n∗k2)O(n*k^2)O(n∗k2)

#include<bits/stdc++.h>usingnamespace std;constint64_t inf =(int64_t)1e18;structW{int t, p, c;W(){}W(int t,int p,int c):t(t),p(p),c(c){}};structE{int64_t p = inf;int64_t c =0;// 重定义<, + booloperator<(const E&rhs)const{return p != rhs.p ? p < rhs.p : c < rhs.c;} E operator+(const E&rhs)const{return{p+rhs.p, c+rhs.c};}};intmain(){int n, k; cin >> n >> k; vector<W>tasks(n);for(int i =0; i < n; i++){ W &w = tasks[i]; cin >> w.t >> w.p >> w.c;}// 预处理,时间和费用的前缀和 vector<int64_t>pre_p(n +1,0); vector<int64_t>pre_c(n +1,0);for(int i =0; i < n; i++){ pre_p[i +1]= pre_p[i]+ tasks[i].p; pre_c[i +1]= pre_c[i]+ tasks[i].c;}// 定义 dp[i][j], 前i项使用j次操作的最小时间/费用  vector<vector<E>>dp(n,vector<E>(k +1));for(int i =0; i <= k && i < n; i++){ dp[i][i]={(pre_p[i +1]- pre_p[0])* tasks[0].t, pre_c[i +1]- pre_c[1]};}// 刷表for(int i =0; i < n; i++){for(int j =0; j <= k; j++){for(int s =0; s + j <= k && i + s +1< n; s++){ E tmp ={(pre_p[i + s +2]- pre_p[i +1])* tasks[i +1].t, pre_c[i + s +2]- pre_c[i +2]}; E tmp2 = dp[i][j]+ tmp;if(tmp2 < dp[i + s +1][j+s]){ dp[i + s +1][j + s]= tmp2;}}}} E ans ={inf,0};for(int i =0; i <= k; i++){if(dp[n -1][i]< ans){ ans = dp[n -1][i];}} cout << ans.p <<" "<< ans.c <<"\n";return0;}

写在最后

在这里插入图片描述

Read more

Linux 进程核心原理精讲:从体系结构到实战操作(含 fork / 状态 / 优先级)----《Hello Linux!》(6)

Linux 进程核心原理精讲:从体系结构到实战操作(含 fork / 状态 / 优先级)----《Hello Linux!》(6)

文章目录 * 前言 * 冯诺依曼体系结构 * 操作系统 * 进程的概念 * 查看进程 * \/proc方法 * ps指令 * 通过系统调用获取进程标示符 * 进程的特性 * 通过系统调用创建进程-fork * 进程状态 * 关于此的几个零碎的知识点 * 具体到Linux的进程状态 * 僵尸进程 * 孤儿进程 * 进程优先级 * 查看系统进程 * 关于PRI和NI * top指令更改nice * 系统如何通过优先级进行调度 前言 进程是操作系统的核心骨架,所有程序的运行本质都是进程的调度与执行。理解进程的底层逻辑,不仅能打通操作系统、硬件与应用程序的关联,更能为排查性能问题、编写高效代码打下基础。 本文将从冯诺依曼体系结构出发,逐步拆解操作系统的核心职责,再深入进程的定义、PCB 结构、状态转换、优先级调度等核心知识点,同时搭配ps/top/fork等实操指令与代码示例,兼顾理论深度与实战性。无论是刚接触 Linux 系统的初学者,还是想夯实底层基础的开发者,

By Ne0inhk
Flutter 组件 short_uuids 适配鸿蒙 HarmonyOS 实战:唯一标识微缩技术,构建高性能短 ID 生成与分布式索引架构

Flutter 组件 short_uuids 适配鸿蒙 HarmonyOS 实战:唯一标识微缩技术,构建高性能短 ID 生成与分布式索引架构

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 short_uuids 适配鸿蒙 HarmonyOS 实战:唯一标识微缩技术,构建高性能短 ID 生成与分布式索引架构 前言 在鸿蒙(OpenHarmony)生态迈向万物互联、涉及海量离线资源标识、蓝牙广播载荷(BLE Payload)及二维码数据极限压缩的背景下,如何生成既能保留 UUID 强随机性、又能极大缩减字符长度的唯一标识符,已成为优化存储与通讯效率的“空间必修课”。在鸿蒙设备这类强调分布式软总线传输与每一字节功耗敏感的环境下,如果应用依然直接传输长度达 36 字符的标准 UUID,由于由于有效载荷溢出,极易由于由于传输协议限制导致数据截断或多次分包带来的延迟。 我们需要一种能够实现高进制转换、支持双向编解码且具备低碰撞概率的短 ID 生成方案。 short_uuids 为 Flutter 开发者引入了将标准 UUID 转化为短格式字符串的高性能算法。它利用

By Ne0inhk
【超详细教程】Claude Code 在 Linux(Ubuntu) 上的完整安装部署指南|一步步跑通云端/本地开发环境

【超详细教程】Claude Code 在 Linux(Ubuntu) 上的完整安装部署指南|一步步跑通云端/本地开发环境

目录 ✅ 逐步安装命令(复制即用) 步骤 1:更新系统环境(强烈建议先执行) 步骤 2:安装 Node.js 步骤 3:安装 Git 步骤 4:安装 Claude Code CLI 步骤 5:配置 Claude Code 的环境变量(核心步骤) 步骤 6:首次运行 Claude Code(本地模式) ✅ 写到最后 之前的文章( Win11 下从零部署 Claude Code )里,我们已经带大家在 Win11 环境下完整跑通了 Claude Code 的本地开发环境,不少朋友后台留言说: “能不能写一个

By Ne0inhk
Flutter 三方库 remove_markdown 的鸿蒙化适配指南 - 打造纯净文本提取、内容预处理实战、鸿蒙级文本解析专家

Flutter 三方库 remove_markdown 的鸿蒙化适配指南 - 打造纯净文本提取、内容预处理实战、鸿蒙级文本解析专家

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 remove_markdown 的鸿蒙化适配指南 - 打造纯净文本提取、内容预处理实战、鸿蒙级文本解析专家 在鸿蒙跨平台应用处理海量的 Markdown 博文、技术文档或用户输入的富文本内容时,有时我们需要剥离所有的样式标记(如加粗、链接、列表),还原出最原始、最纯洁的文字内容。如果你需要为搜索索引构建、智能语音播报(TTS)或是内容摘要生成提供高质量的数据源。今天我们要深度解析的 remove_markdown——一个专注于高效、无损 Markdown 语法剥离的轻量级 Dart 库,正是帮你实现“内容减负”的核心引擎。 前言 remove_markdown 是一套基于正则表达式与高效字符扫描的转换工具。它的设计初衷极其明确:将复杂的 Markdown 源码瞬间坍缩为易于阅读和处理的纯文本。在鸿蒙端项目中,利用它你可以确保在展示搜索片段(

By Ne0inhk