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

RC-u1 热҈热҈热҈

分数 10

作者 DAI, Longao

单位 杭州百腾教育科技有限公司

热҈热҈热҈……最近热得打的字都出汗了!

幸好某连锁餐厅开启了气温大于等于 35 度即可获得一杯免费雪碧的活动。但不知为何,在每个星期四的时候,这个活动会暂停一天……

现在给定连续的若干天的气温情况以及给定的第一天是星期几,请你算出有多少天你可以喝到免费的雪碧,又有多少天是因为星期四而导致你喝不到雪碧的。

输入格式:

输入第一行是两个正整数 N, W (1≤N≤50,1≤W≤7),表示给定连续的 N 天,下面给定的第一天是星期 W(7 等于星期天)。

接下来的一行给出 N 个用一个空格隔开的、小于 60 的整数,第 i 个数表示第 i 天的温度。保证温度大于等于 -273 度。

输出格式:

输出两个数,第一个是你能喝到免费雪碧的天数,第二个是你本来能喝到免费雪碧、但因为是星期四而无法喝到的天数。

输入样例:

15 3
33 35 34 36 37 40 32 31 30 29 28 29 33 38 40

输出样例:

5 1
#include 

using namespace std;

#define endl '\n' 
//#define int long long
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
using PII = pair; 

const int N=1e5+5;

void solve() {
    int n,w;
    cin >> n>>w;
    int t;
    int a=0,b=0;
    for (int i = 0; i < n; i++)
    {
        cin>>t;
        if(t>=35){
            if(w==4){
                b++;
            }else a++;
        }
        w++;
        if(w==8)w=1;
    }
    cout<_/___.' >' "".
*          | | :  `- \`.;`\ _ /`;.`/ - ` : | |
*          \  \ `_.   \_ __\ /__ _/   .-` /  /
*      =====`-.____`.___ \_____/___.-`___.-'=====
*                        `=---='
* 
* 
*      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* 
*            佛祖保佑       永不宕机     永无BUG
*/

 

RC-u2 谁进线下了?

分数 15

作者 DAI, Longao

单位 杭州百腾教育科技有限公司

Xepa Legends 是一个第一人称射击类大逃杀(“吃鸡”)游戏,每轮游戏共有 20 支 3 人小队参加,最后获胜的队伍被称为“捍卫者”。

最近 Xepa Legends 举行了亚太地区南赛区的线上比赛,争夺 7 个前往德国曼海姆参加线下赛的资格,国内共有 14 支队伍参与到了其中。因为比赛十分激烈,直到最后谁进了线下仍有巨大的疑问。小 K 喜欢的国内知名战队 DreamTear 因其队内选手杀马特表现不佳,正好卡在出线分数前后,请你赶紧帮帮小 K,计算一下最后的分数情况,看看他喜欢的战队出线了没有吧!

Xepa Legends 的比赛共进行 N 场游戏,在每场游戏中,每支队伍在游戏中会获得一个排名和一个杀敌数(击败其他队伍玩家的数量),一支队伍在一场游戏的得分为杀敌数+排名分,排名分由队伍当场的排名根据以下表格求得:

排名 分数
第一名 12 分
第二名 9 分
第三名 7 分
第四名 5 分
第五名 4 分
第六名至第七名 3 分
第八名至第十名 2 分
第十一名至第十五名 1 分
第十六名至第二十名 0 分

例如,

  • DreamTear 战队在第三场比赛获得了第三名、有 6 个杀敌数,那么他们将获得 7 + 6 = 13 分;
  • KV 战队在第二场比赛获得了第 19 名、有 1 个杀敌数,那么他们将获得 0 + 1 = 1 分;
  • SRN 战队在第四场比赛获得了第 1 名、有 9 个杀敌数,那么他们将获得 12 + 9 = 21 分。

注:本题与实际情况无关,所有比赛规则、队伍、队员名称均为虚构。

输入格式:

输入第一行是一个正整数 N (≤20),表示有 N 场比赛。

接下来有 N 部分输入,每部分是一场比赛的情况。对每一场比赛,信息共分 20 行,第 i 行(i=1,⋯,20)给出的两个非负整数 p 和 k 表示第 i 支队伍在这场比赛里获得了第 p 名、杀敌数为 k。

数据保证所有给定的情况中,排名永远大于等于 1 且小于等于 20,杀敌数小于等于 57。

输出格式:

输出 20 行,按编号从小到大依次输出队伍的编号及该队全部游戏结束时的总分。

输入样例:

3
6 2
7 3
11 5
10 1
2 9
5 8
14 3
4 3
1 6
18 1
12 1
20 0
13 0
3 2
16 4
8 1
19 0
9 4
17 1
15 0
8 2
19 1
12 2
1 9
10 1
7 5
18 0
14 0
5 2
4 4
2 5
6 2
16 3
13 1
20 0
3 7
9 3
15 0
17 5
11 3
18 0
5 2
2 9
9 4
4 7
10 3
16 0
1 6
20 0
15 1
6 0
3 6
14 3
7 4
19 0
17 0
8 9
11 0
13 5
12 0

输出样例:

1 9
2 13
3 27
4 30
5 33
6 25
7 4
8 27
9 24
10 12
11 19
12 18
13 8
14 18
15 4
16 17
17 16
18 8
19 12
20 6

 

#include 

using namespace std;

#define endl '\n' 
//#define int long long
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
using PII = pair; 

const int N=1e5+5;

int rk(int a){
    if(a==1)return 12;
    if(a==2)return 9;
    if(a==3)return 7;
    if(a==4)return 5;
    if(a==5)return 4;
    if(a==6||a==7)return 3;
    if(a>=8&&a<=10)return 2;
    if(a>=11&&a<=15)return 1;
    return 0;
}

void solve() {
    int n;
    cin >> n;
    int p,k;
    mapmp;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <=20; j++)
        {
            cin>>p>>k;
            mp[j]+=rk(p)+k;
        }
        

    }
    for (auto i :mp)
    {
        cout<_/___.' >' "".
*          | | :  `- \`.;`\ _ /`;.`/ - ` : | |
*          \  \ `_.   \_ __\ /__ _/   .-` /  /
*      =====`-.____`.___ \_____/___.-`___.-'=====
*                        `=---='
* 
* 
*      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* 
*            佛祖保佑       永不宕机     永无BUG
*/

RC-u3 暖炉与水豚

分数 20

作者 DAI, Longao

单位 杭州百腾教育科技有限公司

PapiCon(@PapilloteContet)出了许多有意思的谜题,其中有一道关于水豚的谜题是这样的:

2024 睿抗机器人开发者大赛CAIP-编程技能赛-本科组(省赛)_第1张图片


来源:x.com/PapilloteContet

在一个 N×M 的矩阵中有若干水豚以及暖炉,暖炉可以辐射以它自身为中心的 3×3 范围里的水豚,使其变得暖呼呼的。谜题里存在一只冷的要命的水豚,你需要移动其中的一个暖炉,使所有水豚都变得暖呼呼的。

在往下读题前,如果你有兴趣的话,不妨思考一下如何解答这个谜题。(思考结果与题目无关,可跳过。)

这个谜题的关键在于,单纯从图中能看到的暖炉来说是无解的,但如果注意到,第 3 行第 6 列的水豚明明周围没有暖炉,却也处于暖呼呼的状态,就能推测出来图中的那个对话框挡住了一个暖炉,只要移动这个暖炉就可以完成题目的要求。

现在我们将谜题一般化,对于给定的一个 N×M 的矩阵、对应的所有水豚状态、以及能看到的暖炉摆放情况,已知最多只有一只水豚的状态不太对劲(周围没有暖炉却暖呼呼的),你需要推测有哪些格子可能藏了暖炉。一个空格可能藏了暖炉可以理解为:当前空格设置暖炉后整个矩阵的状态会从不合法变为合法。

输入格式:

输入第一行是两个正整数 N, M (1≤N,M≤1000),表示矩阵的大小。

接下来的 N 行,每行有 M 个字符,第 i 行的第 j 个字符表示矩阵中对应位置的状态,其中:

  • . 表示空格(或者说,看上去是空格的格子);
  • c 表示很冷的水豚;
  • w 表示暖呼呼的水豚;
  • m 表示暖炉。

输出格式:

输出若干行,每行两个正整数 r 和 c,表示第 r 行第 c 列有可能藏了一个暖炉,有多个可能时,先按 r 从小到大输出,r 相同时再按 c 从小到大输出。如果没有一个格子可能藏了暖炉, 则在一行中输出Too cold!
行与列均从 1 开始编号。

输入样例:

6 8
wm....mw
.w..ww..
..wm.wwm
w.w....w
.m.c.m..
w.....w.

输出样例:

2 7
3 5
4 6
4 7
#include 
#include 
using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int N, M;
    cin >> N >> M;
    vector grid(N);
    for(int i = 0; i < N; i++){
        cin >> grid[i];
    }
    
    // 标记所有可见暖炉的覆盖范围
    vector> heated(N, vector(M, false));
    
    // 遍历所有可见暖炉,标记其3×3覆盖范围
    for(int i = 0; i < N; i++){
        for(int j = 0; j < M; j++){
            if(grid[i][j] == 'm'){
                for(int di = -1; di <= 1; di++){
                    for(int dj = -1; dj <= 1; dj++){
                        int ni = i + di, nj = j + dj;
                        if(ni >= 0 && ni < N && nj >= 0 && nj < M){
                            heated[ni][nj] = true;
                        }
                    }
                }
            }
        }
    }
    
    // 找出异常水豚:'w'但不在heated区域内
    int anomaly_r = -1, anomaly_c = -1;
    for(int i = 0; i < N; i++){
        for(int j = 0; j < M; j++){
            if(grid[i][j] == 'w' && !heated[i][j]){
                anomaly_r = i;
                anomaly_c = j;
                break;
            }
        }
        if(anomaly_r != -1) break;
    }
    
    // 标记禁区:冷水豚'c'的3×3邻域不能放暖炉
    vector> forbidden(N, vector(M, false));
    for(int i = 0; i < N; i++){
        for(int j = 0; j < M; j++){
            if(grid[i][j] == 'c'){
                for(int di = -1; di <= 1; di++){
                    for(int dj = -1; dj <= 1; dj++){
                        int ni = i + di, nj = j + dj;
                        if(ni >= 0 && ni < N && nj >= 0 && nj < M){
                            forbidden[ni][nj] = true;
                        }
                    }
                }
            }
        }
    }
    
    // 寻找候选位置
    vector> candidates;
    for(int i = 0; i < N; i++){
        for(int j = 0; j < M; j++){
            if(grid[i][j] == '.' && !forbidden[i][j]){
                if(anomaly_r != -1){
                    // 必须能覆盖异常水豚(在其3×3邻域内)
                    if(abs(i - anomaly_r) <= 1 && abs(j - anomaly_c) <= 1){
                        candidates.push_back({i+1, j+1});
                    }
                } else {
                    // 没有异常水豚,当前状态已合法
                    // 按题意应该输出"Too cold!"
                }
            }
        }
    }
    
    if(candidates.empty()){
        cout << "Too cold!" << endl;
    } else {
        for(auto &p : candidates){
            cout << p.first << " " << p.second << endl;
        }
    }
    
    return 0;
}

 

RC-u4 章鱼图的判断

分数 25

作者 DAI, Longao

单位 杭州百腾教育科技有限公司

对于无向图 G=(V,E),我们将有且只有一个环的、大于 2 个顶点的无向连通图称之为章鱼图,因为其形状像是一个环(身体)带着若干个树(触手),故得名。

给定一个无向图,请你判断是不是只有一个章鱼子图存在。

输入格式:

输入第一行是一个正整数 T (1≤T≤5),表示数据的组数。

每组数据的第一行是两个正整数 N,M (1≤N,M≤105),表示给定的无向图有 N 个点,M 条边。

接下来的 M 行,每行给出一条边两个端点的顶点编号。注意:顶点编号从 1 开始,并且题目保证任何边不会重复给出,且没有自环。

输出格式:

对于每组数据,如果给定的图里只有一个章鱼子图,则在一行中输出 Yes 和章鱼子图环的大小(及环中顶点数),其间以 1 个空格分隔。

否则,则在一行中输出 No 和图中章鱼子图的个数,其间以 1 个空格分隔。

输入样例:

3
10 10
1 3
3 5
5 7
7 9
1 2
2 4
2 6
3 8
9 10
1 9
10 10
1 3
3 5
5 7
7 9
9 1
1 2
2 4
4 8
8 10
10 1
10 10
1 3
3 5
5 7
7 9
9 1
2 4
4 8
8 10
10 2
10 6

输出样例:

Yes 5
No 0
No 2
#include 
#include 
#include 
#include 
using namespace std;

const int NMAX = 100000;

vector> adj;  // 邻接表

// 全局变量用于环检测(只在一个“章鱼图”分量中使用)
int cycleLength;  // 检测到的环长

// dfsCycle 在连通分量内对 u 做 DFS,
// 参数 allowed 指示哪些结点属于当前连通分量。
// par 数组记录每个结点的父亲结点。
// localVisited 记录本 DFS 中的访问情况。
// 如果在 DFS 中发现一个后向边,则通过回溯父亲链计算环长并返回 true。
bool dfsCycle(int u, int parent, const vector& allowed, vector& localVisited, vector& par) {
    localVisited[u] = true;
    par[u] = parent;
    for (int v : adj[u]) {
        if (!allowed[v]) continue;  // 不在当前连通分量内
        if (v == parent) continue;    // 忽略父亲结点
        if (!localVisited[v]) {
            if (dfsCycle(v, u, allowed, localVisited, par))
                return true;
        } else {
            // 发现一个后向边:u -> v,其中 v 已访问且 v 不是 u 的父亲
            // 利用 par 数组回溯计算环长
            int len = 1;
            int cur = u;
            while(cur != v) {
                len++;
                cur = par[cur];
            }
            cycleLength = len;
            return true;
        }
    }
    return false;
}

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

int T;
cin >> T;
while (T--) {
    int n, m;
    cin >> n >> m;
    adj.assign(n+1, vector());
    
    for (int i = 0; i < m; i++){
        int u, v;
        cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    
    // visited 用于遍历整个图,记录全局是否访问过
    vector visited(n+1, false);
    
    int octopusCount = 0;
    int answerCycleLength = 0;  // 如果只有一个章鱼图,记录其环长
    
    // 遍历所有连通分量  
    for (int i = 1; i <= n; i++){
        if (visited[i]) continue;
        // 使用 DFS 获得连通分量中的所有结点
        vector compNodes;
        // 用简单的栈实现 DFS
        vector stack;
        stack.push_back(i);
        visited[i] = true;
        while(!stack.empty()){
            int u = stack.back();
            stack.pop_back();
            compNodes.push_back(u);
            for (int v : adj[u]){
                if (!visited[v]) {
                    visited[v] = true;
                    stack.push_back(v);
                }
            }
        }
        
        // 统计当前连通分量结点数与内部边数(注意边重复计数)
        int Vcomp = compNodes.size();
        int Ecomp = 0;
        for (int u : compNodes) {
            Ecomp += adj[u].size();
        }
        Ecomp /= 2;  // 每条边被计数2次
        
        // 如果结点数小于 3 或边数不等于结点数,则不可能为章鱼图
        if (Vcomp < 3 || Ecomp != Vcomp) continue;
        
        // 对当前连通分量准备标记(allowed 数组:标记哪些结点属于该分量)
        vector allowed(n+1, false);
        for (int u : compNodes) {
            allowed[u] = true;
        }
        
        // 对该分量内重新做 DFS(注意此处需要单独记录访问情况)
        vector compVisited(n+1, false);
        vector par(n+1, -1);
        bool foundCycle = false;
        // 任取分量中的第一个结点开始 DFS
        for (int u : compNodes) {
            if (!compVisited[u]){
                if (dfsCycle(u, -1, allowed, compVisited, par)){
                    foundCycle = true;
                    break;
                }
            }
        }
        // 如果成功检测到唯一环(一定会检测到,因为 Ecomp == Vcomp 证明该分量必定有一个环)
        if (foundCycle){
            octopusCount++;
            // 记录第一个章鱼图的环长
            answerCycleLength = cycleLength;
        }
    }
    
    if (octopusCount == 1)
        cout << "Yes " << answerCycleLength << "\n";
    else
        cout << "No " << octopusCount << "\n";
}
return 0;
}

 

RC-u5 工作安排

分数 30

作者 DAI, Longao

单位 杭州百腾教育科技有限公司

小 K 有 N 项工作等待完成,第 i 项工作需要花 ti​ 单位时间,必须在 di​ 时刻或之前完成,报酬为 pi​。假设小 K 工作时刻从 0 开始,且同一时刻只能做一项工作、工作一旦开始则不可中断或切换至其他工作,请你帮小 K 规划一下如何选择合适的工作,使小 K 可以获得最多的报酬。

输入格式:

输入第一行是一个正整数 T (≤5),表示数据的组数。

接下来有 T 组数据,每组数据第一行是一个正整数 N (≤5000),表示待完成工作的数量。接下来的 N 行,每行三个非负整数 ti​、di​、pi​ (均 ≤5000;1≤i≤N),表示第 i 项工作需要花费的时间、截止时间以及报酬。

输出格式:

对于每组数据,输出小 K 能获得最多的报酬是多少。

输入样例:

3
5
1 2 50
3 3 100
1 5 1
3 2 5000
4 5 30
5
1 2 50
3 3 20
1 5 1
3 2 5000
4 5 30
5
1 2 50
3 3 100
1 5 1
3 2 5000
5 5 800

输出样例:

101
80
800
#include 
#include 
#include 
using namespace std;

struct Job {
    int t, d, p;
};

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

int T;
cin >> T;
while(T--){
    int n;
    cin >> n;
    vector jobs(n);
    int maxDeadline = 0;
    for (int i = 0; i < n; i++){
        cin >> jobs[i].t >> jobs[i].d >> jobs[i].p;
        maxDeadline = max(maxDeadline, jobs[i].d);
    }
    
    // 按截止时间从小到大排序
    sort(jobs.begin(), jobs.end(), [](const Job &a, const Job &b){
        return a.d < b.d;
    });
    
    // 定义dp数组,容量为maxDeadline+1,dp[j]: 完成总时长为j时能够获得的最大收益
    vector dp(maxDeadline + 1, 0);
    
    // 遍历所有任务
    for (int i = 0; i < n; i++){
        int t = jobs[i].t, d = jobs[i].d, p = jobs[i].p;
        // 倒序遍历更新dp,注意 j 必须不超过任务截止时间 d
        for (int j = d; j >= t; j--){
            dp[j] = max(dp[j], dp[j - t] + p);
        }
    }
    
    // 答案为所有时间点下的最大收益
    int ans = 0;
    for (int j = 0; j <= maxDeadline; j++){
        ans = max(ans, dp[j]);
    }
    
    cout << ans << "\n";
}
return 0;
}

 

你可能感兴趣的:(算法,数据结构)