欢乐赛#54 官方题解
2025-08-25 11:01:20
发布于:浙江
赛纲介绍
本次题目的总体题目难度如下,各位选手可以借此评估一下自身的技术水平。
题目编号 | 题目名称 | 题目难度 |
---|---|---|
T1 | 作业计划 | 入门 |
T2 | 扑克积分 | 入门 |
T3 | 配装选择 | 入门 |
T4 | LP统计 | 入门 |
T5 | 晨会列队 | 普及- |
T6 | 翻转 | 普及- |
T1 作业计划
题目大意
总共 天的作业,完成每天的作业需要 小时。阿北计划 天完成,需要算一算每天至少要用多少小时来写作业。
题解思路
本题本质为数学的计算,总共需要 个小时, 天平均每天 。
题目说明需要向上取整,可以使用 ceil()
向上取整函数,也可以使用 if
语句判断是否能够整除,不能整除要额外加 小时。
参考代码
#include <bits/stdc++.h>
using namespace std;
int main() {
int x;
cin>>x;
int ans=ceil(1.0*50*x/30);
/*
int ans=50*x/30;
if(50*x%30!=0){
ans++;
}
*/
cout<<ans<<endl;
return 0;
}
T2 扑克积分
题目大意
分别有对应的分数,总积分为四张牌的分数总和。如果四张牌是同数字会有额外的炸弹积分,如果四张牌中同时有代表大小王的 和 会有额外的王炸积分。
题解思路
本题可以先按照牌面积分进行统计,然后用 if
语句判断是否是炸弹的情况以及是否同时存在大小王,根据判断结果进行额外的积分计算。
炸弹情况需要判断四张牌是否都相同,王炸情况只需要判断最后两张牌的大小即可,因为题目中说明了输入的牌面数字按照从小到大顺序输入。
参考代码
#include <bits/stdc++.h>
using namespace std;
int num[20]={0,15,16,3,4,5,6,7,8,9,10,11,12,13,20,25};
int main() {
int a,b,c,d;
cin>>a>>b>>c>>d;
int ans=0;
/*
if(a==1){
ans=ans+15;
}
else if(a==2){
ans=ans+16;
}
else if(a==14){
ans=ans+20;
}
else if(a==15){
ans=ans+25;
}
else if(a>=3&&a<=13){
ans=ans+a;
}
*/
ans=num[a]+num[b]+num[c]+num[d];
if(a==b&&b==c&&c==d){
ans=ans*5;
}
else if(c==14&&d==15){
ans=ans+160;
}
cout<<ans<<endl;
return 0;
}
T3 配装选择
题目大意
每件装备都会增加暴击率和暴击伤害加成,按照题目中给出的预期伤害计算公式计算选择每件装备时的预期总伤害,记录其中的伤害最大值。
题解思路
本题重点在于将装备属性加上基础属性后要将溢出的暴击率调整为暴击伤害加成,再按照题目描述中的公式进行计算。
参考代码
#include <bits/stdc++.h>
using namespace std;
int n,x,y;
int a[100010];
int b[100010];
int main() {
cin>>n>>x>>y;
int num=0;
int ans=0;
int p=0;//暴击率
int q=0;//暴击伤害加成
for(int i=1;i<=n;i++){
cin>>a[i]>>b[i];
p=x+a[i];
q=y+100+b[i];
//cout<<p<<" "<<q<<endl;
if(p>100){
q=q+(p-100)*3;
p=100;
}
//cout<<p<<" "<<q<<endl;
num=10000*p/100*q/100+10000*(100-p)/100;
ans=max(ans,num);
}
cout<<ans<<endl;
return 0;
}
T4 LP统计
题目大意
给出一行用空格隔开的多个人名字符串,统计每个人名出现的次数。输入只有一行,没有具体签名的个数,最后读取到字符串 0
表示输入结束。
题解思路
一个一个字符串进行输入,直到输入到字符串 0
为止。每个字符串与前面记录过的人名作比较,如果出现过则 增加 ,如果未找到则记录该人名。
本题中不同的人名数量和总人名数量较少,所以可以直接循环比较。如果人名较多,可以考虑使用键值对 map
进行人名比较与次数存储。
参考代码
#include <bits/stdc++.h>
using namespace std;
string s[200010];
int lp[200010];
string st;
int num=0;
int main() {
cin>>st;
while(st[0]!='0'){
int flag=1;
for(int i=0;i<num;i++){
if(st==s[i]){
lp[i]++;
flag=0;
break;
}
}
if(flag==1){
s[num]=st;
lp[num]=1;
num++;
}
cin>>st;
}
for(int i=0;i<num;i++){
cout<<s[i]<<" "<<lp[i]<<endl;
}
return 0;
}
T5 晨会列队
题目大意
给出一个 行 列的矩阵,对每一列进行从小到大的排列, 需要放到每一列的最后几行中。
题解思路
使用排序代码进行排列,并特殊判断 的情况。这里冒泡排序的代码比较合适,判断到 时向后面的行进行交换。
参考代码
#include <bits/stdc++.h>
using namespace std;
int n,m;
int a[110][110];
int main() {
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}
for(int j=1;j<=m;j++){
for(int i=1;i<=n;i++){
for(int k=n;k>i;k--){
if(a[k][j]<a[k-1][j]&&a[k][j]!=0){
swap(a[k][j],a[k-1][j]);
}
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
printf("%4d",a[i][j]);
}
cout<<endl;
}
return 0;
}
T6 翻转
题目大意
给出一个 字符串, 必须将其中的一个 翻转为 。求翻转后连续 个数的乘积。
题解思路
枚举所有 的位置,考虑翻转后对前后两个连续 串的影响。情况有:多一个单独 ,某个连续 长度增加 ,两个连续 串长度合并后再加 。
这里数据范围较小,使用枚举方法。因为其他部分多次重复计算,所以可以预处理各个连续部分的长度和下标范围。
参考代码
#include <bits/stdc++.h>
using namespace std;
string s;
int num=0;
long long ans=9e18;//记录最小结果
struct node{//每组连续的0的起始下标L和结束下标R,共有len个连续的0
int L,R;
long long len=0;
}st[100005];
int main() {
cin>>s;
for(int i=0;i<s.size();i++){
if(s[i]=='0'){
num++;//第1~num组连续的0
st[num].L=i;
while(s[i]=='0'&&i<s.size()){
i++;
st[num].len++;
}
st[num].R=i-1;
}
}
long long tmp=st[1].len;//表示未变化时的结果
for(int i=2;i<=num;i++){
tmp=tmp*st[i].len;
}
int flag=0;//必须要有修改 0时则直接记录答案 1则要保留较小值
if(s[0]=='1'){
if(st[1].L==1){//与第一组连续0相连
ans=tmp/st[1].len*(st[1].len+1);
flag=1;
}
else{//多一个单独的0
ans=tmp*1;
flag=1;
}
}
if(s[s.size()-1]=='1'){
if(st[num].R==s.size()-2){//与最后一组连续0相连
if(flag==1){
ans=min(ans,tmp/st[num].len*(st[num].len+1));
}
else{
ans=tmp/st[num].len*(st[num].len+1);
flag=1;
}
}
else{//多一个单独的0
if(flag==1){
ans=min(ans,tmp*1);
}
else{
ans=tmp*1;
flag=1;
}
}
}
for(int i=2;i<=num;i++){//从第二组开始 考虑在这组前面转化
if(st[i-1].R+1==st[i].L-1){//与前一组只空一个1 转化后相连
if(flag==1){
ans=min(ans,tmp/st[i-1].len/st[i].len*(st[i-1].len+st[i].len+1));
}
else{
ans=tmp/st[i-1].len/st[i].len*(st[i-1].len+st[i].len+1);
flag=1;
}
}
else{//与前一组相隔多个连续1 可以前面长度+1 中间单独1 后面长度+1 三种情况
ans=min(ans,tmp*1);
ans=min(ans,tmp/st[i-1].len*(st[i-1].len+1));
ans=min(ans,tmp/st[i].len*(st[i].len+1));
flag=1;
}
}
cout<<ans;
return 0;
}
全部评论 12
#include <bits/stdc++.h>
using namespace std;
string s;
int num=0;
long long ans=9e18;//记录最小结果
struct node{//每组连续的0的起始下标L和结束下标R,共有len个连续的0
int L,R;
long long len=0;
}st[100005];
int main() {
cin>>s;
for(int i=0;i<s.size();i++){
if(s[i]'0'){
num++;//第1~num组连续的0
st[num].L=i;
while(s[i]'0'&&i<s.size()){
i++;
st[num].len++;
}
st[num].R=i-1;
}
}
long long tmp=st[1].len;//表示未变化时的结果
for(int i=2;i<=num;i++){
tmp=tmp*st[i].len;
}int flag=0;//必须要有修改 0时则直接记录答案 1则要保留较小值 if(s[0]=='1'){ if(st[1].L==1){//与第一组连续0相连 ans=tmp/st[1].len*(st[1].len+1); flag=1; } else{//多一个单独的0 ans=tmp*1; flag=1; } } if(s[s.size()-1]=='1'){ if(st[num].R==s.size()-2){//与最后一组连续0相连 if(flag==1){ ans=min(ans,tmp/st[num].len*(st[num].len+1)); } else{ ans=tmp/st[num].len*(st[num].len+1); flag=1; } } else{//多一个单独的0 if(flag==1){ ans=min(ans,tmp*1); } else{ ans=tmp*1; flag=1; } } } for(int i=2;i<=num;i++){//从第二组开始 考虑在这组前面转化 if(st[i-1].R+1==st[i].L-1){//与前一组只空一个1 转化后相连 if(flag==1){ ans=min(ans,tmp/st[i-1].len/st[i].len*(st[i-1].len+st[i].len+1)); } else{ ans=tmp/st[i-1].len/st[i].len*(st[i-1].len+st[i].len+1); flag=1; } } else{//与前一组相隔多个连续1 可以前面长度+1 中间单独1 后面长度+1 三种情况 ans=min(ans,tmp*1); ans=min(ans,tmp/st[i-1].len*(st[i-1].len+1)); ans=min(ans,tmp/st[i].len*(st[i].len+1)); flag=1; } } cout<<ans; return 0;
}
5天前 来自 广东
1??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
5天前 来自 广东
0
T6 翻转
题目大意
给出一个
01
01 字符串, 必须将其中的一个
1
1 翻转为
0
0。求翻转后连续
0
0 个数的乘积。题解思路
枚举所有
1
1 的位置,考虑翻转后对前后两个连续
0
0 串的影响。情况有:多一个单独
0
0,某个连续
0
0 长度增加
1
1,两个连续
0
0 串长度合并后再加
1
1。这里数据范围较小,使用枚举方法。因为其他部分多次重复计算,所以可以预处理各个连续部分的长度和下标范围。
参考代码
5天前 来自 广东
1❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌
5天前 来自 广东
0
T5 晨会列队
题目大意
给出一个
�
n 行
�
m 列的矩阵,对每一列进行从小到大的排列,
0
0 需要放到每一列的最后几行中。题解思路
使用排序代码进行排列,并特殊判断
0
0 的情况。这里冒泡排序的代码比较合适,判断到
0
0 时向后面的行进行交换。参考代码
#include <bits/stdc++.h>
using namespace std;
int n,m;
int a[110][110];
int main() {
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}for(int j=1;j<=m;j++){ for(int i=1;i<=n;i++){ for(int k=n;k>i;k--){ if(a[k][j]<a[k-1][j]&&a[k][j]!=0){ swap(a[k][j],a[k-1][j]); } } } } for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ printf("%4d",a[i][j]); } cout<<endl; } return 0;
}
5天前 来自 广东
1刷屏吧??❌❌❌❌
5天前 来自 广东
0
T3 配装选择
题目大意
每件装备都会增加暴击率和暴击伤害加成,按照题目中给出的预期伤害计算公式计算选择每件装备时的预期总伤害,记录其中的伤害最大值。题解思路
本题重点在于将装备属性加上基础属性后要将溢出的暴击率调整为暴击伤害加成,再按照题目描述中的公式进行计算。参考代码
#include <bits/stdc++.h>
using namespace std;
int n,x,y;
int a[100010];
int b[100010];
int main() {
cin>>n>>x>>y;int num=0; int ans=0; int p=0;//暴击率 int q=0;//暴击伤害加成 for(int i=1;i<=n;i++){ cin>>a[i]>>b[i]; p=x+a[i]; q=y+100+b[i]; //cout<<p<<" "<<q<<endl; if(p>100){ q=q+(p-100)*3; p=100; } //cout<<p<<" "<<q<<endl; num=10000*p/100*q/100+10000*(100-p)/100; ans=max(ans,num); } cout<<ans<<endl; return 0;
}
T4 LP统计
题目大意
给出一行用空格隔开的多个人名字符串,统计每个人名出现的次数。输入只有一行,没有具体签名的个数,最后读取到字符串 0 表示输入结束。题解思路
一个一个字符串进行输入,直到输入到字符串 0 为止。每个字符串与前面记录过的人名作比较,如果出现过则
�
�
LP 增加
1
1,如果未找到则记录该人名。本题中不同的人名数量和总人名数量较少,所以可以直接循环比较。如果人名较多,可以考虑使用键值对 map 进行人名比较与次数存储。
参考代码
#include <bits/stdc++.h>
using namespace std;
string s[200010];
int lp[200010];
string st;
int num=0;
int main() {
cin>>st;
while(st[0]!='0'){
int flag=1;
for(int i=0;i<num;i++){
if(sts[i]){
lp[i]++;
flag=0;
break;
}
}
if(flag1){
s[num]=st;
lp[num]=1;
num++;
}
cin>>st;
}
for(int i=0;i<num;i++){
cout<<s[i]<<" "<<lp[i]<<endl;
}
return 0;
}5天前 来自 广东
1❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌❌
5天前 来自 广东
0
欢乐赛#54 官方题解
userId_undefined
Sherry快乐小狗时空双修者管理员
2025-08-25 11:01:20发布于:浙江
109
阅读
15
回复
3
点赞
赛纲介绍
本次题目的总体题目难度如下,各位选手可以借此评估一下自身的技术水平。题目编号 题目名称 题目难度
T1 作业计划 入门
T2 扑克积分 入门
T3 配装选择 入门
T4 LP统计 入门
T5 晨会列队 普及-
T6 翻转 普及-
T1 作业计划
题目大意
总共
50
50 天的作业,完成每天的作业需要
�
x 小时。阿北计划
30
30 天完成,需要算一算每天至少要用多少小时来写作业。题解思路
本题本质为数学的计算,总共需要
50
×
�
50×x 个小时,
30
30 天平均每天
50
×
�
÷
30
50×x÷30。题目说明需要向上取整,可以使用 ceil() 向上取整函数,也可以使用 if 语句判断是否能够整除,不能整除要额外加
1
1 小时。参考代码
#include <bits/stdc++.h>
using namespace std;int main() {
int x;
cin>>x;
int ans=ceil(1.050x/30);/* int ans=50*x/30; if(50*x%30!=0){ ans++; } */ cout<<ans<<endl; return 0;
}
T2 扑克积分
题目大意
1
∼
15
1∼15 分别有对应的分数,总积分为四张牌的分数总和。如果四张牌是同数字会有额外的炸弹积分,如果四张牌中同时有代表大小王的
14
14 和
15
15 会有额外的王炸积分。题解思路
本题可以先按照牌面积分进行统计,然后用 if 语句判断是否是炸弹的情况以及是否同时存在大小王,根据判断结果进行额外的积分计算。炸弹情况需要判断四张牌是否都相同,王炸情况只需要判断最后两张牌的大小即可,因为题目中说明了输入的牌面数字按照从小到大顺序输入。
参考代码
#include <bits/stdc++.h>
using namespace std;
int num[20]={0,15,16,3,4,5,6,7,8,9,10,11,12,13,20,25};
int main() {
int a,b,c,d;
cin>>a>>b>>c>>d;int ans=0; /* if(a==1){ ans=ans+15; } else if(a==2){ ans=ans+16; } else if(a==14){ ans=ans+20; } else if(a==15){ ans=ans+25; } else if(a>=3&&a<=13){ ans=ans+a; } */ ans=num[a]+num[b]+num[c]+num[d]; if(a==b&&b==c&&c==d){ ans=ans*5; } else if(c==14&&d==15){ ans=ans+160; } cout<<ans<<endl; return 0;
}
5天前 来自 广东
1别ctrl c+=ctrl v了!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
5天前 来自 广东
0
T6代码好长
5天前 来自 广东
0呵,T5稍微处理一下就能直接用sort()了。
6天前 来自 广东
0666,T6直接暴力也能过
6天前 来自 上海
0啥时候AC君写好获奖名单呀,他是不是睡了?
6天前 来自 浙江
0666,第六题我用暴力写都能对
6天前 来自 浙江
0+1
6天前 来自 浙江
0
我勒个冒泡排序
1周前 来自 广东
0我勒个T6啊 芥末长
6天前 来自 浙江
0甚至可以不加判断,先给空位(身高为的)赋很大的值(大于的整数即可),最后除以你赋的值取余即可。
#include<iostream> #include<algorithm> using namespace std; int n,m,a[105][105]; int main(){ cin>>n>>m; for(int i=0;i<n;i++){ for(int j=0;j<m;j++){ cin>>a[j][i]; if(a[j][i]==0) a[j][i]=10000;//没错,就是在这里 } } for(int i=0;i<m;i++) sort(a[i],a[i]+n); for(int i=0;i<n;i++){ for(int j=0;j<m;j++){ printf("%4d",a[j][i]%10000);//搞不懂T5为啥这么长 } cout<<endl; } return 0; }
6天前 来自 广东
0甜菜
6天前 来自 广东
0
%%%
1周前 来自 浙江
0我一直很想问 %%% 是什马意思?
6天前 来自 浙江
0意为膜拜
6天前 来自 浙江
0谢谢
6天前 来自 浙江
0
有帮助,赞一个