本次我使用的是c++,但基本上没用到c++的特性,c语言也完全可以用我的思路。
B-1 被3整除
已知一个正整数能被 3 整除,要满足的条件是其各位数字之和能被 3 整除。
本题就请你判断任一给定正整数是否能被 3 整除。
输入格式:
输入第一行给出一个正整数 n(≤10),随后 n 行,每行给出一个正整数。题目保证每个整数都不超过 104 位。
输出格式:
对每个输入的整数,如果能被 3 整除,就在对应的一行中输出 Yes
,否则输出 No
。随后空 1 格,输出这个整数各位数字的和。
输入样例:
3
1234567890
98765432123
7257482459821364032585428796
输出样例:
Yes 45
No 50
No 136
思路 string存储+暴力。
这一题重点就是每一个整数的位数不超过1e4,已经超过了long long 型所能存储的最大位数,所以我们需要用string型来保存数据。
保存以后遍历数据并计算是否是3的倍数。
c代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main() {
int n,sum;
scanf("%d",&n);
char s[1010];
for(int i=0; i<n; i++) {
sum=0;
scanf("%s",&s);
for(int j=0; j<strlen(s); j++) {
sum+=s[j]-'0';
}
if(sum%3==0) {
printf("Yes %d\n",sum);
} else {
printf("No %d\n",sum);
}
}
return 0;
}
c++代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,sum;
cin>>n;
string s;
for(int i=0;i<n;i++){
sum=0;
cin>>s;
for(int j=0;j<s.size();j++){
sum+=s[j]-'0';
}
if(sum%3==0){
cout<<"Yes "<<sum<<endl;
} else {
cout<<"No "<<sum<<endl;
}
}
return 0;
}
B-2 生日推算
有一道推理题的原文是这样的:甲、乙、丙、丁、戊的生日是在连续的五天里,先后顺序满足以下条件:
- 甲比丙大几天,乙就比戊小几天;
- 丁比戊大 2 天;
- 丙的生日是在星期三。
问另外四人的生日分别在星期几?
现在我们把条件一般化,任意打乱他们的顺序,任意给定其中一人的生日,请你编写程序推算另外四人的生日分别在星期几。
注意到在上述条件描述中,只有“戊”这个人是没有任何信息直接给出的,其他人都有一个与他人关系或自己生日的描述。
输入格式:
输入分 5 行,每行给出一个人的已知信息。其中:
- 如果是一个 [1, 7] 区间内的整数,则表示这个人的生日已知。数字 1 到 7 顺次表示星期一到星期日。
- 如果是一个问号
?
,则表示这个人是原文中的“戊”,没有任何信息直接给出。 - 如果是
-2
,则表示这个人比?
对应的人大 2 天。 - 如果是
-x
,则表示这个人比那个生日已知的人大x
天。 - 如果是
+x
,则表示这个人比?
对应的人小x
天。
输出格式:
在一行中按照输入的顺序给出对应每个人的生日,数字间和行首尾都不要有空格。
输入样例:
3
?
-2
-x
+x
输出样例:
32714
注意:星期一的前一天是星期日。样例输出中,第三个人最大,是星期日过生日。
思路 简单的数学思维
这一题重点就是找到甲乙丙丁戊五人之间的关系。
首先,由题意可知:
然后因为五天是连续的
所以
这就是五天的之间的关系
知道五天之间的关系以后,代码就变得十分简单了。
c代码
#include<stdio.h>
#include<string>
#include<stdlib.h>
int main() {
int c[5],h;
char s[5][2];
for(int i=0; i<5; i++) {
scanf("%s",&s[i]);
//保存已知量
if(s[i][0]>='1'&&s[i][0]<='7') {
c[i]=s[i][0]-'0';
h=c[i];
}
}
//通过已知的数来判断其他的天数
//重点就是星期一的前一天是星期日
for(int i=0; i<5; i++) {
if(s[i][0]>='1'&&s[i][0]<='7') {
continue;
}
if(s[i][0]=='?') {
if(h!=1)
c[i]=h-1;
else
c[i]=7;
} else if(s[i][1]=='2') {
if(h>3)
c[i]=h-3;
else
c[i]=h-3+7;
} else if(s[i][0]=='-') {
if(h>2)
c[i]=h-2;
else
c[i]=h-2+7;
} else if(s[i][0]='+') {
if(h!=7)
c[i]=h+1;
else
c[i]=1;
}
}
for(int i=0; i<5; i++) {
printf("%d",c[i]);
}
return 0;
}
c++代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
int c[5],h;
string s[5];
for(int i=0;i<5;i++){
cin>>s[i];
//保存已知量
if(s[i][0]>='1'&&s[i][0]<='7'){
c[i]=s[i][0]-'0';
h=c[i];
}
}
//通过已知的数来判断其他的天数
//重点就是星期一的前一天是星期日
for(int i=0;i<5;i++){
if(s[i][0]>='1'&&s[i][0]<='7'){
continue;
}
if(s[i][0]=='?'){
if(h!=1)
c[i]=h-1;
else
c[i]=7;
}
else if(s[i][1]=='2'){
if(h>3)
c[i]=h-3;
else
c[i]=h-3+7;
} else if(s[i][0]=='-'){
if(h>2)
c[i]=h-2;
else
c[i]=h-2+7;
} else if(s[i][0]='+'){
if(h!=7)
c[i]=h+1;
else
c[i]=1;
}
}
for(int i=0;i<5;i++){
cout<<c[i];
}
return 0;
}
B-3 计算小达人争霸赛
“计算小达人”争霸赛开始啦!因为参加比赛的小朋友太多了,老师们来不及人工判题,就请你写个程序来帮忙。你要根据每位小朋友的答题记录判断他/她答对了多少题,并且给出冠军的名字。
输入格式:
输入第一行给出 2 个正整数 N(≤1000)和 M(≤100),分别为参赛小朋友的人数和每人回答的问题数。接下来给出 N 个小朋友的答题记录,格式为:
小朋友姓名
A1 + B1 = C1
A2 + B2 = C2
...
AM + BM = CM
其中小朋友姓名
是不含空格、仅由英文字母组成的、不超过 20 个字符的字符串。随后的 M 行,每行给出一个加法算式,算式中的每个数字都是两位数的正整数,数字和加号、等号间都有 1 个空格分隔。
输出格式:
判断每个加法算式是否正确,从而统计出每个小朋友做对了多少题。做对最多题数的是冠军。题目保证冠军只有 1 个。输出的格式为:首先按照输入的顺序,每行输出一个小朋友的姓名和他/她做对的题目数,其间以 1 个空格分隔。最后一行输出:Guan Jun Shi XXX!
,这里 XXX
就是冠军的姓名。
输入样例:
4 3
XiaoHua
12 + 34 = 46
21 + 39 = 50
45 + 28 = 12
XiaoMing
22 + 33 = 55
58 + 13 = 71
67 + 30 = 97
XiaoLiang
30 + 20 = 40
18 + 19 = 35
37 + 42 = 77
XiaoHong
25 + 36 = 61
81 + 12 = 93
17 + 48 = 55
输出样例:
XiaoHua 1
XiaoMing 3
XiaoLiang 0
XiaoHong 2
Guan Jun Shi XiaoMing!
思路 模拟
一个最基本的模拟题
重点 考虑名字之间没有空格和 符号数字之间有空格 需要用不同的方法输入
因为告诉了我们每个数字都是两位数,所以可以直接计算。
c代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main() {
int n,m,maxn=0;
char s[15],t[15],tt[15];
scanf("%d %d",&n,&m);
for(int i=0; i<n; i++) {
int sum=0;
scanf("%s",&s);
getchar();
//读取换行符
for(int j=0; j<m; j++) {
//因为含有空格 直接gets
gets(t);
int a,b,c;
//直接计算 a b c
a=10*(t[0]-'0')+(t[1]-'0');
b=10*(t[5]-'0')+(t[6]-'0');
c=10*(t[10]-'0')+(t[11]-'0');
if(a+b==c) {
sum++;
}
//因为只有一个冠军 所以这里用<= 防止只有一个小朋友且得分为0 这样会没有答案
if(maxn<=sum) {
strcpy(tt,s);
maxn=sum;
}
}
printf("%s %d\n",s,sum);
}
printf("Guan Jun Shi %s!",tt);
return 0;
}
c++代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,m,maxn=0;
string s,t,tt,str="\n";
cin>>n>>m;
for(int i=0;i<n;i++){
int sum=0;
cin>>s;
//读取换行符
getline(cin,str);
for(int j=0;j<m;j++){
//因为含有空格 所以直接getline
getline(cin,t);
int a,b,c;
//直接计算 a b c
a=10*(t[0]-'0')+(t[1]-'0');
b=10*(t[5]-'0')+(t[6]-'0');
c=10*(t[10]-'0')+(t[11]-'0');
if(a+b==c){
sum++;
}
//因为只有一个冠军 所以这里用<= 防止只有一个小朋友且得分为0 这样会没有答案
if(maxn<=sum){
tt=s;
maxn=sum;
}
}
cout<<s<<" "<<sum<<endl;
}
cout<<"Guan Jun Shi "<<tt<<"!";
return 0;
}
B-4 分组数列
一个分组数列是按以下规律定义的:第 1 组包含 1 个数字,为 0×3+1;第 2 组包含 2 个数字,为 0×3+2 和 1×3+2;第 3 组包含 3 个数字,为 1×3+3, 2×3+3 和 3×3+3;以此类推。
一般来说,第 i 组包含 i 个数字,从上一组最后一个数字加 1 开始,每次增 3。例如第 3 组可以理解为:包含 3 个数字,从第 2 组最后一个数字 5 加 1(即 6)开始,增 3 得到第 2 个数字为 9,再增 3 得到第 3 个数字为 12。
所以根据定义,这个序列前 10 个数字就是:1, 2, 5, 6, 9, 12, 13, 16, 19, 22。它们实际上分成了 4 个组,即:{1}, {2, 5}, {6, 9, 12}, {13, 16, 19, 22}。
本题就请你找出任意一个给定数字是这个数列的第几项。
输入格式:
输入第一行首先给出一个正整数 n (≤105),是待查询的数字个数。随后 n 行,每行给出一个查询的正整数,所有数字都不超过 105。
输出格式:
对每个查询的正整数,输出其在数列中的序号(从 1 开始)。如果这个数字不在数列中,则输出 Not Found
。
输入样例:
4
12
16
5941
87654
输出样例:
6
8
2023
Not Found
思路 数据预处理
对于这种查询问题的题 我们可以先将数据进行预处理 直接把1-1e5分别对于的位置算出来
这样可以极大的节省时间 重新查询会超时。
c代码
#include<stdio.h>
const int N=1e5;
//这里用2*N是为了防止数据超出
int n,sum[2*N],he,tt;
int main() {
scanf("%d",&n);
int t=1,j=1;
he=0;
//对数据进行预处理
while(he<=N) {
he+=1;
sum[he]=j;
j++;
for(int i=1; i<t; i++) {
he+=3;
sum[he]=j;
j++;
}
t++;
}
//直接查询 不再处理数据
for(int i=0; i<n; i++) {
scanf("%d",&tt);
if(sum[tt]!=0) {
printf("%d\n",sum[tt]);
} else {
printf("Not Found\n");
}
}
return 0;
}
c++代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5;
//这里用2*N是为了防止数据超出
int n,sum[2*N],he,tt;
int main()
{
cin>>n;
int t=1,j=1;
he=0;
//对数据进行预处理
while(he<=N){
he+=1;
sum[he]=j;
j++;
for(int i=1;i<t;i++){
he+=3;
sum[he]=j;
j++;
}
t++;
}
//直接查询 不再处理数据
for(int i=0;i<n;i++){
cin>>tt;
if(sum[tt]!=0){
cout<<sum[tt]<<endl;
} else {
cout<<"Not Found"<<endl;
}
}
return 0;
}
B-5 两头进一头出
某队列允许在其两端进行入队操作,但仅允许在一端进行出队操作。现给定入队的序列,请你判断一系列出队序列是否可能。例如按 1、2、3、4、5 的顺序入队,则 1、3、2、5、4 这样的出队序列是可以得到的,但 5、1、3、2、4 就是不可能得到的。
输入格式:
输入首先在一行中给出两个正整数 N 和 K(≤10),分别是入队元素的个数和待查验的序列个数。随后一行给出 N 个两两不同的整数(每个都不超过 106)组成的入队序列;再跟着 K 行,每行给出由 N 个入队整数组成的出队序列。同行整数间以空格分隔。
输出格式:
对每个需要查验的出队序列,如果是可能的,则在一行中输出 yes
,否则输出 no
。
输入样例:
5 4
10 2 3 4 5
10 3 2 5 4
5 10 3 2 4
2 3 10 4 5
3 5 10 4 2
输出样例:
yes
no
yes
no
思路 模拟+dfs
因为这道题的数据很小(1-n),那么深搜(dfs)的时间复杂度(o!)也满足要求
我的想法:首先题目的进队顺序是固定的 但没有要求入队完以后再出队 可以随时出队
那我们在满足出队顺序要求的时候直接出队 不会影响后面的顺序
这样我们就进行深度搜索 将所有的情况列出来 判断是否有这种情况
通过数组来模拟队列
c代码
#include<stdio.h>
const int N=1e5;
//sum 存储的是入队顺序 t存储的是出队顺序 h存储的是出队的所有可能性
int n,k,sum[11],t[11],h[22];
bool p;
//i代表入队的位数 j代表出队的位数 r代表往左放 l代表往右放
void dfs(int i,int j,int r,int l) {
//如果满足出队顺序 p为真
if(j==k) {
p=true;
return;
}
//如果满足出列的部分条件 直接出列
if(h[11-r+1]==t[j]) {
h[11-r+1]=0;
dfs(i,j+1,r-1,l);
}
//只有当上面还能有放的情况下才能继续放
if(i<n) {
//如果队列里面没有数
if(r==0&&l==0) {
//以11为中心
h[11]=sum[i];
dfs(i+1,j,r+1,l);
}
//如果现在下面需要的数等于上面的数 那么我们就放在左边
//这样可以极大的节省时间
else if(sum[i]==t[j]) {
h[11-r]=sum[i];
dfs(i+1,j,r+1,l);
}
//如果没有特殊情况
//这个是放在左边
h[11-r]=sum[i];
dfs(i+1,j,r+1,l);
//这个是放在右边
h[11+l+1]=sum[i];
dfs(i+1,j,r,l+1);
}
return;
}
int main() {
scanf("%d %d",&n,&k);
for(int i=0; i<n; i++) {
scanf("%d",&sum[i]);
}
//假设左端为出口
for(int i=0; i<k; i++) {
for(int j=0; j<n; j++) {
scanf("%d",&t[j]);
}
//先放再判断
//数据初始化
p=false;
for(int k=0; k<22; k++) {
h[k]=0;
}
dfs(0,0,0,0);
if(p) {
printf("yes\n");
} else {
printf("no\n");
}
}
return 0;
}
c++代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5;
//sum 存储的是入队顺序 t存储的是出队顺序 h存储的是出队的所有可能性
int n,k,sum[11],t[11],h[22];
bool p;
//i代表入队的位数 j代表出队的位数 r代表往左放 l代表往右放
void dfs(int i,int j,int r,int l){
//如果满足出队顺序 p为真
if(j==k){
p=true;
return;
}
//如果满足出列的部分条件 直接出列
if(h[11-r+1]==t[j]){
h[11-r+1]=0;
dfs(i,j+1,r-1,l);
}
//只有当上面还能有放的情况下才能继续放
if(i<n){
//如果队列里面没有数
if(r==0&&l==0){
//以11为中心
h[11]=sum[i];
dfs(i+1,j,r+1,l);
}
//如果现在下面需要的数等于上面的数 那么我们就放在左边
//这样可以极大的节省时间
else if(sum[i]==t[j]){
h[11-r]=sum[i];
dfs(i+1,j,r+1,l);
}
//如果没有特殊情况
//这个是放在左边
h[11-r]=sum[i];
dfs(i+1,j,r+1,l);
//这个是放在右边
h[11+l+1]=sum[i];
dfs(i+1,j,r,l+1);
}
return;
}
int main()
{
cin>>n>>k;
for(int i=0;i<n;i++){
cin>>sum[i];
}
//假设左端为出口
for(int i=0;i<k;i++){
for(int j=0;j<n;j++){
cin>>t[j];
}
//先放再判断
//数据初始化
p=false;
for(int k=0;k<22;k++){
h[k]=0;
}
dfs(0,0,0,0);
if(p){
cout<<"yes"<<endl;
} else {
cout<<"no"<<endl;
}
}
return 0;
}
总结
总体难度不算高,但最后一题比较难。
第一题就考察大数据的存储
第二题就考察的数学思维 考虑到五人之间的天数关系
第三题就考察的模拟,模拟的难度也不算大,需要注意细节
第四题考察的是数据预处理,对于多次询问,我们该如何处理
第五题考察的是dfs/(学长说的是大模拟,可以用set来解决,但我懒得想了)
如果有什么不懂的话或者更好的想法,欢迎评论区讨论!