L1-043 阅览室
C++代码
cpp
// 考虑最后一次借书和第一次还书,以及round用法
#include <bits/stdc++.h>
using namespace std;
int n;
map<int,char>mp;
map<int,int>mp1;
signed main()
{
scanf("%d",&n);
int cnt=0;
int sum=0;
int ci=0;
while(1)
{
int id;
char ch;
int h,m;
scanf("%d %c",&id,&ch);
scanf("%d:%d",&h,&m);
if(id==0)
{
if(ci==0){
printf("0 0\n");
}
else
{
int u=round(sum*1.0/ci);
printf("%d %d\n",ci,u);
}
sum=0;
ci=0;
cnt++;
mp.clear();
mp1.clear();
if(cnt==n){
break;
}
}
else
{
char pre=mp[id];
if(pre!='S'&&pre!='E')
{
if(ch=='S'){
mp[id]=ch;
mp1[id]=h*60+m;
}
}
else if(pre=='S')
{
if(ch=='E'){
mp[id]='E';
sum=sum+h*60+m-mp1[id];
ci++;
}
else
{
mp[id]='S';
mp1[id]=h*60+m;
}
}
else if(pre=='E'){
if(ch=='S'){
mp[id]=ch;
mp1[id]=h*60+m;
}
}
}
}
return 0;
}
Python代码
python
n=int(input())
cnt=0
sum=0
ci=0
mp={}
mp1={}
while(1):
id,ch,time=map(str,input().split())
id=int(id)
h,m=time.split(":")
h=int(h)
m=int(m)
total_min = h * 60 + m # 提前计算总分钟数,简化代码
if(id==0):
if(ci==0):
print("0 0")
else:
u=int(round(sum*1.0/ci))
print(f"{ci} {u}")
sum=0
ci=0
cnt+=1
mp.clear()
mp1.clear()
if(cnt==n):
break
else:
# 处理mp中无id的情况(对应C++的默认空字符)
pre = mp.get(id, '') # 无key时返回空字符串,而非报错
if pre not in ('S', 'E'): # 首次访问或非S/E状态
if ch == 'S':
mp[id] = ch
mp1[id] = total_min
elif pre == 'S': # 上一次是借书状态
if ch == 'E': # 本次还书,统计时长
mp[id] = 'E'
sum += total_min - mp1[id]
ci += 1
else: # 再次借书,覆盖时间
mp[id] = 'S'
mp1[id] = total_min
elif pre == 'E': # 上一次是还书状态
if ch == 'S': # 重新借书
mp[id] = ch
mp1[id] = total_min
mp[id]=ch
mp1[id]=h*60+m
思路
初始化准备 :首先读取需要统计的批次总数
n,同时初始化统计变量(借还总时长sum、有效借还次数ci、已完成批次计数器cnt),以及两个字典缓存(mp存储每本图书的当前借还状态,mp1存储每本图书最后一次借书的时间)。循环处理借还记录 :持续读取每一条借还记录,直至完成
n个批次的统计:
- 解析每条记录:将记录中的图书编号
id转为整数,借还状态ch保留字符串,时间拆分为小时和分钟后统一转换为总分钟数,简化后续时间差计算。- 终止符处理(
id=0):触发当前批次的结算逻辑 ------ 若该批次无有效借还记录(ci=0),输出 "0 0";若有有效记录,计算并输出有效借还次数ci和四舍五入后的平均借阅时长(总时长sum除以次数ci后四舍五入并转为整数)。结算后重置总时长、有效次数,清空两个字典缓存,已完成批次计数器加 1,若计数器达到n则终止循环。- 有效图书记录处理(
id≠0):先安全获取该图书的历史借还状态(无历史状态则返回空字符串),再按状态规则更新:
- 首次处理该图书(无历史状态):仅当本次状态为借书(
S)时,记录该图书的状态为S,并缓存本次借书的总分钟数。- 历史状态为借书(
S):若本次状态为还书(E),则标记该图书状态为E,计算本次借阅时长(当前时间减缓存的借书时间)并累加到总时长,有效借还次数加 1;若本次仍为借书(S),则覆盖缓存的借书时间,更新状态为S。- 历史状态为还书(
E):仅当本次状态为借书(S)时,重新记录该图书的状态为S,并缓存本次借书的总分钟数。批次隔离:每完成一个批次的结算,都会清空存储状态和时间的字典缓存,确保不同批次的借还记录数据隔离,互不干扰。
数值计算规则 :平均时长计算时,通过
sum*1.0/ci确保浮点数除法,再用round四舍五入后转为整数,与 C++ 原版的数值计算逻辑保持一致。
L1-044 稳赢
C++代码
cpp
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
signed main()
{
cin>>n;
int cnt=0;
while(1)
{
string s;
cin>>s;
if(s=="End"){
break;
}
else
{
if(cnt==n){
cnt=0;
cout<<s<<"\n";
}
else
{
cnt++;
if(s=="ChuiZi"){
cout<<"Bu\n";
}
else if(s=="JianDao"){
cout<<"ChuiZi\n";
}
else if(s=="Bu"){
cout<<"JianDao\n";
}
}
}
}
return 0;
}
Python代码
python
n=int(input())
cnt=0
while(1):
s=input()
if(s=="End"):
break
else:
if(cnt==n):
cnt=0
print(s)
else:
cnt+=1
if(s=="ChuiZi"):
print("Bu")
elif(s=="JianDao"):
print("ChuiZi")
elif(s=="Bu"):
print("JianDao")
思路
初始化准备 :首先读取一个整数
n(表示每轮循环的阈值),并初始化计数器cnt(用于记录当前已处理的非阈值轮次数量)。循环处理输入:持续读取输入字符串,直至输入 "End" 时终止循环:
- 终止条件判断:若输入字符串为 "End",直接跳出循环,结束程序。
- 非终止输入处理:
- 若计数器
cnt等于阈值n:说明已完成一轮n次的规则转换,此时重置计数器cnt为 0,并直接输出当前输入的字符串(不执行手势转换)。- 若计数器
cnt小于阈值n:计数器cnt加 1,然后按 "石头剪刀布" 的转换规则输出对应结果:
- 输入 "ChuiZi(锤子)"→ 输出 "Bu(布)";
- 输入 "JianDao(剪刀)"→ 输出 "ChuiZi(锤子)";
- 输入 "Bu(布)"→ 输出 "JianDao(剪刀)"。
核心逻辑特点:
- 以
n为周期循环:每累计处理n个非终止输入后,第n+1个输入会触发 "重置计数器 + 直接输出" 的逻辑,之后重新开始按规则转换。- 规则转换仅针对 "ChuiZi、JianDao、Bu" 三种固定输入,未提及的其他输入(若有)会无输出(代码未处理该情况)。
- 终止信号 "End" 具有最高优先级,无论当前计数器状态如何,输入后立即终止程序。
L1-046 整除光棍
C++代码
cpp
#include <bits/stdc++.h>
using namespace std;
int r=0;
vector<int>div(vector<int>a,int b)
{
r=0;
vector<int>c;
for(int i=0;i<a.size();i++)
{
r=r*10+a[i];
c.push_back(r/b);
r%=b;
}
reverse(c.begin(),c.end());
while(c.size()>1&&c.back()==0){
c.pop_back();
}
return c;
}
signed main()
{
int b;
cin>>b;
string s="1";
while(1)
{
vector<int>a;
for(int i=0;i<s.size();i++){
a.push_back(s[i]-'0');
}
vector<int>c=div(a,b);
if(r==0){
for(int i=c.size()-1;i>=0;i--){
cout<<c[i];
}
cout<<" ";
cout<<s.size();
break;
}else{
s+="1";
}
}
return 0;
}
Python代码
python
r=0
b=input()
s="1"
def div(a,b):
global r
r=0
c=[]
b=int(b)
for i in range(0,len(a)):
r=r*10+a[i]
c.append(r//b)
r=r%b
c.reverse()
while(len(c)>1 and c[-1]==0):
c.pop()
return c
while(1):
a=[]
for i in range(0,len(s)):
a.append(int(s[i]))
c=div(a,b)
if(r==0):
for i in range(len(c)-1,-1,-1):
print(c[i],end="")
print(end=" ")
print(len(s),end="")
break
else:
s+="1"
思路
代码核心思路
该代码的核心目标是:找到由连续数字 "1" 组成的最小正整数(如 1、11、111......),使其能被输入的整数
b整除,最终输出该数除以b的商,以及这个由 "1" 组成的数的位数。整体逻辑分为 "手工除法模拟" 和 "候选数枚举" 两大核心模块,具体如下:1. 初始化准备
- 定义全局余数变量
r(初始为 0),用于存储除法运算后的余数,判断候选数是否能被b整除;- 读取输入的除数
b(字符串类型,后续在除法函数内转为整数);- 初始化候选数字符串
s为 "1"(表示第一个待验证的数是 1)。2. 核心除法函数
div(a, b)(模拟手工大整数除法)该函数实现 "大整数(由列表
a存储各位数字)除以整数b" 的手工计算过程,核心目的是计算商和余数:
- 声明使用全局变量
r,重置r为 0,初始化存储商的列表c;- 将除数
b转为整数类型,避免字符串运算错误;- 逐位处理大整数(列表
a的每一位):① 累计余数:r = r*10 + 当前位数字(模拟手工除法 "带下一位" 的操作,将当前余数与下一位数字合并);② 计算当前位商:将r//b(整除结果)加入商列表c;③ 更新余数:r = r%b(保留当前位除法后的余数,用于后续位计算);- 商列表修正:反转商列表(修正手工除法的位序),并去除列表末尾的前导零(仅保留有效商位数,如商列表 [0,0,7] 转为 [7]);
- 返回修正后的商列表,同时通过全局变量
r传递最终余数。3. 候选数枚举循环(寻找符合条件的 "全 1 数")
通过无限循环构造由 "1" 组成的候选数(1→11→111→......),直至找到能被
b整除的数:
- 候选数转换:将当前字符串
s(如 "111")转为整数列表a(如 [1,1,1]),适配除法函数的输入格式;- 调用除法函数:传入列表
a和除数b,获取商列表c,并通过全局变量r得到余数;- 终止条件判断:若余数
r=0(当前候选数能被b整除):① 逆序输出商列表的每一位(还原为正常数字顺序);② 输出空格和当前候选数s的长度(即由多少个 "1" 组成);③ 终止循环;- 候选数扩展:若余数
r≠0(当前候选数无法整除),则在s末尾追加 "1"(构造下一个候选数,如 "11"→"111"),继续循环验证。4. 核心逻辑特点
- 采用 "手工除法" 而非直接大整数运算,还原了底层计算过程,避免因超大数运算导致的性能或格式问题;
- 以 "枚举 + 验证" 的方式寻找最小 "全 1 数",逻辑简单且贴合手工计算思维;
- 通过全局变量传递余数,确保除法函数的计算结果能被主循环感知,控制循环终止。
L1-049 天梯赛座位分配
C++代码
cpp
#include <bits/stdc++.h>
#define int long long
#define x first
#define y second
using namespace std;
const int N=10000;
vector<int>ans[N];
int n;
int cnt[N];
map<int,bool>mp;
signed main()
{
cin>>n;
for(int i=1;i<=n;i++){
cin>>cnt[i];
cnt[i]*=10;
}
if(n==1)
{
int st=1;
while(ans[1].size()<cnt[1]){
ans[1].push_back(st);
st+=2;
}
}
else
{
int id=1;
int st=1;
int sum=0;
int u=0;
while(1)
{
sum++;
if(ans[id].size()>=cnt[id]&&mp[id]==false){
mp[id]=true;
u++;
}
if(sum>10000000) break;
if(ans[id].size()<cnt[id])
{
if(u==n-1){
st++;
}
ans[id].push_back(st);
id++;
st++;
}
else{
id+=1;
}
if(id>n) id=1;
}
}
for(int i=1;i<=n;i++)
{
cout<<"#"<<i<<"\n";
vector<int>a=ans[i];
for(int j=0;j<a.size();j++){
cout<<a[j];
if(j%(10)==9) cout<<"\n";
else cout<<" ";
}
}
}
Python代码
python
n=int(input())
cntinput=input().split()
mp={}
ans=[[] for i in range(10000)]
cnt=[0]*100000
for i in range(0,n):
cnt[i+1]=int(cntinput[i])
cnt[i+1]*=10
if(n==1):
st=1
while(len(ans[1])<cnt[1]):
ans[1].append(st)
st+=2
else:
id=1
st=1
sum=0
u=0
while(1):
sum+=1
if(len(ans[id])>=cnt[id] and mp.get(id,0)==False):
mp[id]=True
u+=1
if(sum>100000): break
if(len(ans[id])<cnt[id]):
if(u==n-1):
st+=1
ans[id].append(st)
id+=1
st+=1
else:
id+=1
if(id>n): id=1
for i in range(1,n+1):
print(f"#{i}")
a=ans[i]
for j in range(0,len(a)):
print(a[j],end="")
if(j%10==9): print()
else: print(end=" ")
思路
代码核心思路
该代码的核心目标是:按指定规则为 n 个分组填充连续递增的数值,每个分组需填充至指定长度(输入值 ×10),最终按格式输出每个分组的填充结果。整体逻辑分为 "输入处理、特殊场景填充、普通场景循环填充、结果输出" 四部分,具体如下:
1. 初始化与输入处理
- 读取分组数量
n,以及每个分组的原始填充长度(存入cntinput);- 初始化核心数据结构:
ans:长度为 10000 的列表的列表,ans[i]存储第i个分组的填充数值;cnt:长度为 100000 的数组,cnt[i]存储第i个分组的目标填充长度(原始长度 ×10),索引从 1 开始匹配分组编号;mp:空字典,用于标记已填满的分组(键为分组 id,值为布尔值)。- 遍历输入的原始长度,转换为整数后 ×10,存入
cnt[1]~cnt[n](对齐分组编号)。2. 特殊场景处理(n=1)
仅当分组数为 1 时,单独填充第一个分组:
- 初始化起始值
st=1;- 循环向
ans[1]中添加数值,每次添加后st+2(仅填充奇数),直到ans[1]的长度达到cnt[1](目标长度)。3. 普通场景处理(n>1):循环填充多个分组
通过无限循环按规则填充各分组,直到循环次数超过 100000(防死循环):
- 初始化变量:
id(当前填充的分组编号,初始为 1)、st(当前要填充的数值,初始为 1)、sum(循环计数器,初始为 0)、u(已填满的分组数量,初始为 0);- 每次循环先将
sum+1,执行以下逻辑:
- 分组填满标记:若当前分组
id的填充长度≥目标长度,且未被标记,则标记该分组为已填满,u+1;- 防死循环退出:若
sum超过 100000,强制终止循环;- 填充当前分组:若当前分组未填满:
- 若只剩最后一个分组未填满(
u=n-1),st仅 + 1(无额外递增);- 向当前分组添加
st,id+1(切换下一个分组),st+1;- 跳过已填满分组:若当前分组已填满,仅将
id+1;- 分组循环:若
id超过总分组数n,重置id为 1(循环填充)。4. 结果输出
按格式输出每个分组的填充结果:
- 遍历每个分组(1~n),先输出
#i(i 为分组编号);- 遍历该分组的填充数值,逐个输出:
- 每输出 10 个数值后换行;
- 未到 10 个且非最后一个数值时,输出空格分隔;
- 最后一个数值后不输出多余空格,若最后一行不足 10 个数值,补换行。
核心逻辑特点
- 分组填充采用 "循环轮询" 方式,确保每个分组按规则填充至目标长度;
- 对 n=1 的场景单独处理,填充奇数;n>1 时动态调整数值递增规则(只剩最后一个分组时仅 + 1);
- 用字典标记已填满分组,避免重复统计;设置循环次数上限,防止死循环;
- 输出格式严格对齐 "每 10 个换行、数值间空格分隔" 的规则,保证输出整洁。
L1-050 倒数第N个字符串
C++代码
cpp
// 看成是正数的第几个,考虑26进制即可,注意不足n位前面补a;
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,tar;
map<int,char>mp;
signed main()
{
char ch='a';
for(int i=1;i<=26;i++){
mp[i]=ch++;
}
cin>>n>>tar;
int x=pow(26,n)-tar;
vector<int>ans;
if(x==0){
for(int i=0;i<n;i++){
cout<<"a";
}
}
else
{
while(x)
{
int u=x%26;
u++;
ans.push_back(u);
x/=26;
}
if(ans.size()<n){
int u=n-ans.size();
for(int i=0;i<u;i++) cout<<"a";
}
for(int i=ans.size()-1;i>=0;i--){
cout<<mp[ans[i]];
}
}
return 0;
}
Python代码
python
import math
mp={}
ch='a'
id=ord(ch)
for i in range(1,27):
mp[i]=chr(id)
id+=1
n,tar=map(int,input().split())
x=math.pow(26,n)-tar
ans=[]
if(x==0):
for i in range(0,n):
print("a",end="")
else:
while(x):
u=x%26
u+=1
ans.append(u)
x=x//26
if(len(ans)<n):
u=n-len(ans)
for i in range(0,u):
print("a",end="")
for i in range(len(ans)-1,-1,-1):
print(mp[ans[i]],end="")
思路
代码核心思路
该代码的核心目标是:根据输入的位数
n和目标值tar,计算并输出一个长度为n的小写字母字符串,核心逻辑是将26^n - tar的结果按特殊规则转换为 26 进制字母表示(不足n位时前面补 'a')。整体流程分为 "映射初始化、核心计算、特殊值处理、进制转换、补位输出" 五部分,具体如下:1. 数字 - 字母映射初始化
构建 1 到 26 与小写字母 a 到 z 的一一对应关系:
- 初始化空字典
mp,以数字 1~26 为键,对应的小写字母 a~z 为值;- 通过 ASCII 码操作生成字母:先获取 'a' 的 ASCII 码,循环 1~26 时,每次将 ASCII 码 + 1 并转回字符,存入字典(如
mp[1]='a'、mp[2]='b'...mp[26]='z')。2. 输入处理与核心数值计算
- 读取两个整数输入:
n(最终输出字符串的固定长度)、tar(目标偏移值);- 计算核心值
x = 26^n - tar(即 26 的 n 次方减去 tar),将问题转化为对x的 26 进制转换。3. 特殊值处理(x=0)
若
x等于 0,直接输出n个连续的 'a'(因为 26^n - tar=0 意味着 tar=26^n,对应最小的字母组合 "aaa...a")。4. 26 进制分解(偏移调整)
若
x≠0,对x进行 26 进制分解,且每一步做偏移调整:
- 初始化空列表
ans存储分解后的数字;- 循环分解
x:
- 取
x除以 26 的余数u,将u+1(偏移调整,使余数范围从 0~25 转为 1~26,匹配字典的键范围);- 将调整后的
u存入ans列表;- 将
x更新为x除以 26 的整数商(继续分解高位);- 直到
x变为 0 时停止分解。5. 补位与结果输出
将分解后的数字转换为字母并输出,确保总长度为
n:
- 补位:若
ans列表的长度(分解后的位数)小于n,计算需要补充的 'a' 的数量(n - len(ans)),并连续输出对应数量的 'a';- 逆序输出:逆序遍历
ans列表(因为进制分解是从低位到高位存储),根据字典mp将每个数字转换为对应字母并输出,最终拼接成长度为n的字符串。核心逻辑特点
- 本质是 "反向 26 进制转换":常规 26 进制是 0~25 对应 a~z,此处通过
余数+1调整为 1~26 对应 a~z,适配 "26^n - tar" 的计算逻辑;- 补位规则保证输出字符串长度严格等于
n,不足时前置补 'a',符合 "不足 n 位前面补 a" 的要求;- 利用 ASCII 码生成字母映射,避免手动枚举,简洁且不易出错。