2024 睿抗编程设计赛道 本科组省赛补题()

545 阅读11分钟

先说结果,AC三道,后两题骗了两个测试点,最终47分(好菜 中位线45分)

开始的时候又是OMS的日常崩,都开始20多分钟才登进去。

RC-u1 热҈热҈热҈…

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

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

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

输入格式:

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

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

输出格式:

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

输入样例:

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

输出样例:

5 1
AC代码
#include<bits/stdc++.h>
using namespace std;
int main(){
    int n,w,Free_days=0,Mis_Thu=0,tem;
    cin >> n >> w;
    for(int i=0;i<n;++i){
        cin >> tem;
        if(w==8) w=1;
        if(tem>=35 && w!=4) Free_days++;
        if(tem>=35 && w==4) Mis_Thu++;
        w++;
    }
    cout << Free_days << " " << Mis_Thu <<endl;
    return 0;
}

签到题


RC-u2 谁进线下了?

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

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

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

image.png

例如,

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

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

输入格式:

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

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

数据保证所有给定的情况中,排名永远大于等于 11 且小于等于 2020,杀敌数小于等于 5757

输出格式:

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

输入样例:

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
AC代码:
#include<bits/stdc++.h>
using namespace std;
int n,p,k;
int contingent[21];
int ranking[21]={0,12,9,7,5,4,3,3,2,2,2,1,1,1,1,1};
int main(){
    cin >> n;
    while(n--){
        for(int i=1;i<21;++i){
            cin >> p >> k;
            contingent[i]+=ranking[p]+k;
        }
    }
    for(int id=1;id<=20;++id)
        cout << id << " " << contingent[id] << endl;
    return 0;
}

先建立一个ranking数组存放排名得分就好做了,签到题


RC-u3 暖炉与水豚

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

GGwLLL_bwAA8cC4.jpeg

来源:x.com/PapilloteContet

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

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

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

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

输入格式:

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

接下来的 NN 行,每行有 MM 个字符,第 ii 行的第 jj 个字符表示矩阵中对应位置的状态,其中:

  • . 表示空格(或者说,看上去是空格的格子);
  • 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
AC代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,i,j,k,X,Y,exist;
int x[8]={-1,-1,-1,0,0,1,1,1};
int y[8]={-1,0,1,-1,1,-1,0,1};
char mat[1005][1005];
int jud[1005][1005];
int main(){
    cin >> n >> m;
    for(i=1;i<=n;++i)
        for(j=1;j<=m;++j)
            cin >> mat[i][j];
    for(i=1;i<=n;++i)
        for(j=1;j<=m;++j)
            if(mat[i][j]=='m'){
                jud[i][j]=1;
                for(k=0;k<8;++k)
                    jud[i+x[k]][j+y[k]]=1;
            }
    for(i=1;i<=n;++i)
        for(j=1;j<=m;++j)
            if(mat[i][j]=='w'&&jud[i][j]==0){
                X=i,Y=j;
                for(k=0;k<8;++k)
                    if(mat[i+x[k]][j+y[k]]=='.')
                        jud[i+x[k]][j+y[k]]=2;
            }
    if(!X)
        cout << "Too cold!";
    else{
        for(i=1;i<=n;++i)
            for(j=1;j<=m;++j)
                if(mat[i][j]=='c')
                    for(k=0;k<8;++k)
                        jud[i+x[k]][j+y[k]]=-1;
        for(i=0;i<8;++i)
            if(jud[X+x[i]][Y+y[i]]==2){
                exist=1;
                cout << X+x[i] << " " << Y+y[i] << endl;
            }
        if(!exist) cout << "Too cold!";
    }
    return 0;
}

比赛时我就纯模拟,所以代码量有点大,因为要遍历矩阵 3 次更新jud数组最终得到结果。

最开始设置的 X,YX,Y 数组原因如下:

image.png

在输入矩阵时我从下标1开始输入,这样之后遍历就不用考虑越界之类的问题也减少了代码量。


RC-u4 章鱼图的判断

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

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

输入格式:

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

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

接下来的 MM 行,每行给出一条边两个端点的顶点编号。注意:顶点编号从 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

比赛时骗了1分,之后补上AC代码

AC代码:
#include<bits/stdc++.h>
using namespace std;
const long long N = 1e5+5;
int t,n,m,i;
int p[N],sz[N],d[N];
int find(int x){
    if(x!=p[x]) p[x]=find(p[x]);
    return p[x];
}
int main(){
    cin >> t;
    while(t--){
        cin >> n >> m;
        vector<int> g[n+5];
        for(i=1;i<=n;++i)
            p[i]=i,d[i]=-1,sz[i]=0;
        int ans=0,st,ed;
        while(m--){
            int a,b;
            cin >> a >> b;
            g[a].push_back(b);
            g[b].push_back(a);
            int fa=find(a),fb=find(b);
            if(fa==fb){
                sz[fa]++;
                st=a,ed=b;
            }
            else{
                p[fa]=fb;
                sz[fb]+=sz[fa];
            }
        }
        for(i=1;i<=n;++i)
            if(find(i)==i && sz[i]==1)
                ans++;
        if(ans==1){
            queue<int> q;
            q.push(st);
            d[st]=1;
            while(!q.empty()){
                int u=q.front();
                q.pop();
                for(auto&v:g[u]){
                    if(u==st && v==ed)
                        continue;
                    if(d[v]==-1){
                        d[v]=d[u]+1;
                        q.push(v);
                    }
                }
            }
            cout << "Yes " << d[ed] << endl;
        }
        else
            cout << "No " << ans << endl;
    }
    return 0;
}

运用并查集的方法


RC-u5 工作安排

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

输入格式:

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

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

输出格式:

对于每组数据,输出小 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

一样也骗了一分,之后补上

AC代码21/30代码,两个测试点没过:
#include<bits/stdc++.h>
using namespace std;
typedef struct node{
    int t,d,p;
}N;
bool cmp(N x,N y){
    return x.d<y.d;
}
int T,n,i,j,k,ti,di,pi;
int main(){
    cin >> T;
    while(T--){
        cin >> n;
        N a[5005];
        int dp[5005]={0};
        for(i=1;i<=n;++i)
            cin >> a[i].t >> a[i].d >> a[i].p;
        sort(a+1,a+n+1,cmp);
        for(i=1;i<=n;++i){
            ti=a[i].t,di=a[i].d,pi=a[i].p;
            for(j=di;j>=ti;--j)
                dp[j]=max(dp[j],dp[j-ti]+pi);
            for(j=1;j<=n;++j)
                dp[j]=max(dp[j-1],dp[j]);
        }
        cout << dp[n] << endl;
    }
    return 0;
}

01背包的模型,和cmp排序的使用,解题时选择先解决早截止的防止后效性