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

Qwen3-VL + LLama-Factory进行针对Grounding任务LoRA微调

Qwen3-VL + LLama-Factory进行针对Grounding任务LoRA微调

0.官方GitHub网站: GitHub - QwenLM/Qwen3-VL:Qwen3-VL 是由阿里云 Qwen 团队开发的多模态大语言模型系列。https://github.com/QwenLM/Qwen3-VL 空间感知能力大幅提升:2D grounding 从绝对坐标变为相对坐标,支持判断物体方位、视角变化、遮挡关系,能实现 3D grounding,为复杂场景下的空间推理和具身场景打下基础。 OCR 支持更多语言及复杂场景:支持的中英外的语言从 10 种扩展到 32 种,覆盖更多国家和地区;在复杂光线、模糊、倾斜等实拍挑战性场景下表现更稳定;对生僻字、古籍字、专业术语的识别准确率也显著提升;超长文档理解和精细结构还原能力进一步提升。 一是采用 MRoPE-Interleave,原始MRoPE将特征维度按照时间(t)、高度(h)和宽度(w)的顺序分块划分,

AI辅助编程的边界探索:当Copilot学会写测试

AI辅助编程的边界探索:当Copilot学会写测试

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕人工智能这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获! 文章目录 * AI辅助编程的边界探索:当Copilot学会写测试 🚀 * 1. 从“写代码”到“验代码”:AI的新战场 ⚔️ * 场景设定:一个简单的支付网关模拟器 💳 * 2. 初级实验:AI能写出“Happy Path”吗? ✅ * 3. 进阶实验:Mocking 与 外部依赖 🎭 * 4. 陷阱与幻觉:AI写测试时犯的那些错 🤪 * 案例 A:永远不会错的测试 * 案例 B:永远跑不通的断言 * 案例 C:复杂集成测试的无力 * 5. 人机协作:重新定义测试工作流 🤝 * 实践技巧:如何高效地让AI写测试?

Lostlife2.0下载官网整合LLama-Factory引擎,增强NPC对话逻辑

Lostlife2.0整合LLama-Factory引擎,重塑NPC对话逻辑 在文字冒险游戏的世界里,玩家最怕什么?不是任务太难,也不是剧情平淡——而是和一个“话术机械、反应呆板”的NPC对话时,那种瞬间出戏的割裂感。明明世界观设定是末世废土,结果NPC张口就是“绝绝子”“破防了”,这种语言风格的崩塌足以让沉浸感荡然无存。 《Lostlife2.0》作为一款以深度叙事和角色互动为核心卖点的文字冒险游戏,在开发过程中就直面了这一难题。早期版本中,NPC的对话依赖传统的决策树系统:每句台词都由编剧手动编写,每个分支都需要精确配置。这不仅导致内容维护成本极高,更带来了“选项爆炸”问题——新增一条剧情线,往往要额外添加数十个节点,最终形成一张难以管理的复杂网络。 真正的转机出现在团队引入 LLama-Factory 之后。这个开源的大模型微调框架,原本主要用于科研与企业级AI定制,但《Lostlife2.0》团队敏锐地意识到:它或许能成为解决NPC智能瓶颈的关键工具。通过将LLama-Factory深度集成到开发流程中,他们成功构建了一套动态、可进化、风格一致的对话生成系统,彻底改变了传

GPU PRO 4 - 5.1 An Aspect-Based Engine Architecture 笔记

本笔记仅为个人的理解,如果有误欢迎指出 An Aspect-Based Engine Architecture 一种基于方面的引擎架构         不是很明白为什么GPU的书籍会有游戏引擎架构的文章。         这里Aspect在文章中的意义更像是表述一个功能模块,在Java中有将Aspect翻译成切面,但是Java切面主要是横向的代码注入,与本文的概念不相符。 大多数系统架构都会考虑将各个功能封装成模块或者组件,在面向对象编程的思想下,这个封装是基于对象去实现的,本文则描述了一种在引擎层面的封装功能的架构思想,封装后的产物被称为Aspect,每一个Aspect负责提供一些功能子集,并通过一个通用的接口与引擎核心通信。 引擎核心:         引擎核心的功能是保存游戏或者仿真时的数据结构以及相关状态,功能Aspect将会与这些数据进行交互。一般来说引擎核心会定义一些接口,外部的Aspect则通过接口访问当前的游戏数据                  用MVC架构的角度去理解的话引擎核心相当于M层,而各个Aspect则相当于C层。