2024 RAICOM CAIP(本科组)省赛个人题解

RC-u1 热҈热҈热҈

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

幸好某连锁餐厅开启了气温大于等于 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<bits/stdc++.h>
using namespace std;

int n,d;
int sum,res;

int main(void){
	cin>>n>>d;
	for(int i = 1 ; i <= n ; i ++){
		int t;cin>>t;
		if(t >= 35 && d != 4) sum++;
		if(t >= 35 && d == 4) res++;
		if(d + 1 == 8) d = 1;
		else d++;
	}
	cout<<sum<<" "<<res;
	return 0;
}

RC-u2 谁进线下了?

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)给出的两个非负整数 pk 表示第 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<bits/stdc++.h>
using namespace std;

int n;
int sco[21];

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

int main(void){
	cin>>n;
	for(int i = 1 ; i <= n ; i ++){
		for(int j = 1 ; j <= 20 ; j ++){
			int p,k;cin>>p>>k;
			sco[j] += getScore(p);
			sco[j] += k;
		}
	}
	for(int i = 1 ; i <= 20 ; i ++){
		cout<<i<<" "<<sco[i]<<endl;
	}
	return 0;
}

RC-u3 暖炉与水豚(模拟)

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

GGwLLL_bwAA8cC4.jpeg

来源:x.com/PapilloteContet

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

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

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

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

输入格式

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

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

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

输出格式

输出若干行,每行两个正整数 rc,表示第 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

解法

Step1:找到不合法的水豚:温暖,但是没被暖炉照到

Step2:找到该水豚周围,所有可放置暖炉的位置,并判断是否合法

Step3:存储所有暖炉的合法位置,逐个输出

代码

#include<bits/stdc++.h>
using namespace std;
const int MAX = 1050;

const int dirx[9] = {0,-1,-1,-1,0,0,1,1,1};
const int diry[9] = {0,-1,0,1,-1,1,-1,0,1};
int n,m;
char a[MAX][MAX];
bool isWarm[MAX][MAX];
vector<pair<int,int> > ans;

void putLight(int x,int y){ //放下暖炉后温暖9格区域
	for(int i = 0 ; i < 9 ; i ++){
		int nx = x + dirx[i];
		int ny = y + diry[i];
		if(nx >= 1 && nx <= n && ny >= 1 && ny <= m){
			isWarm[nx][ny] = true;
		}
	}
}

bool judge(int x,int y){ //放置的暖炉是否合法
	for(int i = 1 ; i < 9 ; i ++){
		int nx = x + dirx[i];
		int ny = y + diry[i];
		if(nx >= 1 && nx <= n && ny >= 1 && ny <= m){
			if(a[nx][ny] == 'c') return false;
		}
	}
	return true;
}

void findPos(int x,int y){//找到所有可放置暖炉的位置并判断
	for(int i = 1 ; i < 9 ; i ++){
		int nx = x + dirx[i];
		int ny = y + diry[i];
		if(nx >= 1 && nx <= n && ny >= 1 && ny <= m){
			if(a[nx][ny] == '.' && judge(nx,ny)){
				ans.push_back({nx,ny});
			}
		}
	}
}

int main(void){
	cin>>n>>m;
	for(int i = 1 ; i <= n ; i ++){
		for(int j = 1 ; j <= m ; j ++){
			cin>>a[i][j];
			if(a[i][j] == 'm') putLight(i,j);
		}
	}
    //找到不合法的豚鼠:温暖,但是没被暖炉照到
	for(int i = 1 ; i <= n ; i ++){
		for(int j = 1 ; j <= m ; j ++){
			if(a[i][j] == 'w' && !isWarm[i][j]){
				findPos(i,j);
			}
		}
	}
	if(ans.empty()) cout<<"Too cold!"<<endl;
	else{
		for(auto it : ans){
			cout<<it.first<<" "<<it.second<<endl;
		}
	}
	return 0;
}

RC-u4 章鱼图的判断(并查集+BFS)

对于无向图 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

解法

题中说的“章鱼子图”,实际上是:“是章鱼图的连通分量”

如果出现两个环,要么有两张章鱼子图,要么原图不是章鱼子图,一定不符合题目条件

Step1:并查集求每个连通分量的环的个数,并记录环的两个端点

Step2:BFS求环的长度

代码

#include<bits/stdc++.h>
using namespace std;
const int MAX = 1e5 + 10;

int n,m,t;
int r1,r2; //环的两个端点
int pre[MAX],cnt[MAX],d[MAX]; //cnt -- 连通子图 i 内环的个数
bool visited[MAX];
vector<int > e[MAX];

int find(int x){
	if(x == pre[x]) return x;
	else return pre[x] = find(pre[x]);
}

int bfs(int s,int t){//给两个端点,求环的长度
	queue<int > q;
	q.push(s);
	memset(visited,0,sizeof(visited));
	memset(d,0,sizeof(d));
	while(!q.empty()){
		int f = q.front();q.pop();
		visited[f] = true;
		for(auto it : e[f]){
			if(!visited[it]){
				if(it == t && f == s) continue;//切断环的两个端点之边
				d[it] = d[f] + 1;
				q.push(it);
			}
		}
	}
	return d[t]+1;
}

void solve(){
	cin>>n>>m;
	for(int i = 1 ; i <= n ; i ++){
		pre[i] = i;
		e[i].clear();
	}
	memset(cnt,0,sizeof(cnt));
	for(int i = 1 ; i <= m ; i ++){
		int a,b;cin>>a>>b;
		e[a].push_back(b);
		e[b].push_back(a);
		
		int fa = find(a);
		int fb = find(b);
		
		if(fa == fb){ //若已经连通的两点之间,存在一条边,那么存在环
			cnt[fb]++;
			r1 = a;
			r2 = b;
		}else{ //合并
			pre[fa] = fb;
			cnt[fb] += cnt[fa];
		}
	}
	int sum = 0;
	for(int i = 1 ; i <= n ; i ++){
		if(find(i) == i && cnt[i] == 1){
			sum++;
		}
	}
	if(sum == 1){
		cout<<"Yes "<<bfs(r1,r2)<<endl;
	}else{
		cout<<"No "<<sum<<endl;
	}
}

int main(void){
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

RC-u5 工作安排(01背包)

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

输入格式

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

接下来有 T 组数据,每组数据第一行是一个正整数 N (≤5000),表示待完成工作的数量。接下来的 N 行,每行三个非负整数 t**id**ip**i (均 ≤5000;1≤iN),表示第 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

解法

状态转移方程: d p [ j ] = m a x ( d p [ j ] , d p [ t − t a [ i ] . d ] + t a [ i ] . p ) dp[j] = max(dp[j], dp[t - ta[i].d] + ta[i].p) dp[j]=max(dp[j],dp[tta[i].d]+ta[i].p)

dp[j] : 到达时间j时,能获得的最大报酬

ta[i].d:任务i的截止时间

ta[i].p:任务i的报酬

与普通的01背包不同的地方在于,

这题为每个任务规定的能采用的时间上限,即:截止时间 - 任务时间,

因此,为了保证每个任务都在优化的考虑范围内,需要根据截止时间对任务进行排序。

代码

#include<bits/stdc++.h>
using namespace std;
const int MAX = 5050;

struct task{
	int t,d,p;
	bool operator<(const task & rhs){
		return d < rhs.d;
	}
}ta[MAX];

int t,n;
int dp[MAX];

void solve(){
	cin>>n;
	for(int i = 0 ; i < n ; i ++){
		cin>>ta[i].t>>ta[i].d>>ta[i].p;
	}
	sort(ta,ta+n);
	memset(dp,0,sizeof(dp));
	int ans = 0;
	for(int i = 0 ; i < n ; i ++){
		for(int j = ta[i].d ; j >= ta[i].t ; j --){
			dp[j] = max(dp[j],dp[j - ta[i].t] + ta[i].p);
			ans = max(dp[j],ans);
		}
	}
	cout<<ans<<endl;
}

int main(void){
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}
  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值