目录
- [Problem 1110](https://xjoi.net/problem/1110) 某年某月有几天
- [Problem 9358](https://xjoi.net/problem/9358) Maoge的游戏
- [Problem 9354](https://xjoi.net/problem/9354) maoge的硬币
- [Problem 1197](https://xjoi.net/problem/1197) 小帅打电话
- [Problem 9302](https://xjoi.net/problem/9302) 自动找人系统
- [Problem 3904](https://xjoi.net/problem/3904) 年龄计算
Problem 1110 某年某月有几天
一 题目内容
时间:1s 空间:256M
题目描述:
闰年问题:已知某年某月,计算该月有几天。
输入格式:
输入一行,包含两个正整数
y
e
a
r
,
m
o
n
t
h
year,month
year,month ,代表年和月。
输出格式:
输出一行,包含一个整数。
样例输入:
2004
2004
2004
2
2
2
样例输出:
29
29
29
二 新知识点
2.1 一维数组
当我们要记录
4
4
4 个数据时,我们可以使用 a0
,a1
,a2
,a3
四个变量进行记录。但如果数据大小开到
100
100
100,
1000
1000
1000,乃至
1
0
6
10^6
106 呢?是不是还要用“开
1
0
6
10^6
106 个变量”这种原始的方法呢?
答案当然是否定的。这时要引入一个新的概念——一维数组。一个一位数组中可以包含许多个元素(即变量)。一维数组在内存上是连续的,也就是说,你可以直接在与访问一个变量相同的时间内访问数组的某一个元素。其声明如下所示:
int a[10]; //当然也可以换成别的数据类型
这句代码的意思是,开一个长度为
10
10
10 的一维数组。在介绍其操作之前,先介绍“下标”的概念。“下标”指访问的元素编号,即现在访问的是第几个元素。
一维数组的基本操作有以下几种(假设 a 数组已声明):
(1)访问某一下标的元素的值:
cout<<a[0];
注意,数组下标从
0
0
0 开始,即
0
0
0 号元素才是一个数组实际上的开始。因此,上面例子中的 a[10]
实际上包含的下标是
0
∼
9
0 \sim 9
0∼9。
(2)输入数组中的
n
n
n 个数(下标为
0
∼
n
−
1
0 \sim n-1
0∼n−1):
for(int i=0;i<n;i++){
cin>>a[i];
}
这表明数组的某个下标是可以单独输入的,如:
cin>>a[0]; //输入a数组的第0个下标的值
当然,字符数组也可以像这样输入:
cin>>a;
这样,字符数组 a 中会存入整个字符串(每个下标只有一个字符)加上一个 ‘\0’ 表示终止符。所以,如果要像上例一样输入字符数组,记得把数组开大点!
(3)数组的初始化:
//方法一:给数组全部元素全部赋值为空(字符'',布尔值false,整数与浮点数0)
int a[10]={};
//方法二:给部分或全部元素赋值
int b[7]={1,2,3,4,5,6,7}; //实际上是初始化了b数组下标0到6的值,而不是下标1到7的值(况且b数组根本没有下标7!)
char c[10]={'I','O','I'}; //c数组中只会有'I','O','I'三个字符,不会有终止符'\0'
三 思路
判断一年
y
e
a
r
year
year 是否为闰年的方法如下:
(1)若
y
e
a
r
%
4
!
=
0
year\%4!=0
year%4!=0,则该年不是闰年。
(2)若
y
e
a
r
%
4
=
=
0
year\%4==0
year%4==0 但
y
e
a
r
%
100
!
=
0
year\%100!=0
year%100!=0,则该年是闰年。
(3)若
y
e
a
r
%
100
=
=
0
year\%100==0
year%100==0 但
y
e
a
r
%
400
!
=
0
year\%400!=0
year%400!=0,则该年不是闰年。
(4)若
y
e
a
r
%
400
=
=
0
year\%400==0
year%400==0,则该年是闰年。
随后开一维数组记录
12
12
12 个月的天数(
2
2
2 月特殊处理,存为
0
0
0),最后访问数组并输出即可(同样地,
2
2
2 月特殊处理)。
四 AC代码
#include<bits/stdc++.h>
using namespace std;
int main(){
int y,m; //year和month的简写
cin>>y>>m;
int d[13]={0,31,0,31,30,31,30,31,31,30,31,30,31}; //存储每个月的天数
//注意,月份从1开始,但数组下标从0开始,因此要在下标0处填入一个无意义的数值进行占位
if(m==2){
if((year%4==0&&year%100!=0)||(year%400==0)){ //判断闰年(特殊处理2月)
cout<<29;
}else{
cout<<28;
}
}else{
cout<<d[m];
}
return 0;
}
Problem 9358 Maoge的游戏
一 题目内容
【题目描述】
maoge 和 maoge233 玩一个游戏。
他有两堆石子,每次可以选择任意一堆取任意个(至少一个),当一个人没有石子可取时,就输了。
maoge 先取,问最后谁能赢。假设都是以最优策略。
【数据格式】
输入第一行两个数
x
,
y
x,y
x,y 表示两堆石子分别的数量(
x
,
y
≤
100000
x,y \leq 100000
x,y≤100000)
输出 maoge
或者 maoge233
。
样例输入:
1
1
1
2
2
2
样例输出:
m
a
o
g
e
maoge
maoge
二 新知识点
本题没有新知识点。
三 思路
注:本题涉及到博弈论有关知识。
考虑 maoge 如何使自己必胜:设法使场上只剩一堆石子,这时轮到自己取时就一定会赢。可以证明,这是唯一的必胜方法,可惜我不会证明。因为 maoge 先手,所以它具有主动权。他可以使两堆石子的个数相等,这样就可以使用“copy 大法”进行博弈,即如果 maoge233 在一堆石子中取了
a
a
a 个,那么 maoge 就可以在另一堆石子中取
x
x
x 个。可以证明,这是一种必胜策略,因为最终场上一定会只剩一堆石子,可惜我仍然不会证明。但如果开始时两堆石子数量相等,那么 maoge 只能被逼着令两堆石子数量不等,maoge233 就可以再次使两堆石子数量相等,再运用“copy 大法”使自己胜利。
四 AC代码
#include<bits/stdc++.h>
using namespace std;
int main(){
int x,y;
cin>>x>>y;
if(x==y){ //第二种情况,maoge233必胜
cout<<"maoge233";
}else{ //第一种情况,maoge必胜
cout<<"maoge";
}
return 0;
}
//所以你思路分析讲了那么多,结果代码就这么点?
//是的......
Problem 9354 maoge的硬币
一 题目内容
【题目描述】
maoge 的桌子上有
4
4
4 枚硬币,有些正面朝上,有些反面朝上。现在 maoge 想请你帮他把这些硬币都变成同一面朝上,但是你每次能且只能翻三个硬币,请你求出最少翻转次数。
【数据格式】
输入一行,四个数字
0
0
0 或
1
1
1,表示每个硬币的初始状态。
输出一个数,表示答案。
样例输入:
1
1
1
0
0
0
1
1
1
1
1
1
样例输出:
1
1
1
二 新知识点
本题没有新知识点。
三 思路
显然,当场上的
1
1
1 个数相同时,所需的翻转次数也相同。因此,我们只需考虑以下五种情况:
(1)场上全是
0
0
0,此时不用翻硬币。
(2)场上有一个
1
1
1,三个
0
0
0,此时把三个
0
0
0 变成
1
1
1即可,共翻转
1
1
1 次。
(3)场上有两个
1
1
1 和两个
0
0
0,此时显然不能只翻一次硬币了,至少翻转两次,方法如下所示(以初始状态
0
0
0
0
0
0
1
1
1
1
1
1 为例):
初始状态:
0
0
0
0
0
0
1
1
1
1
1
1
翻转一次:
1
1
1
0
0
0
0
0
0
0
0
0
翻转两次:
1
1
1
1
1
1
1
1
1
1
1
1
(4)场上有三个
1
1
1 和一个
0
0
0,分析大体同(2)。
(5)场上全是
1
1
1,分析大体同(1)。
四 AC代码
#include<bits/stdc++.h>
using namespace std;
int main(){
int a,b,c,d;
cin>>a>>b>>c>>d;
int s=a+b+c+d; //求场上1的数量
if(s==0||s==4){
cout<<0;
}else if(s==1||s==3){
cout<<1;
}else{
cout<<2;
}
return 0;
}
Problem 1197 小帅打电话
一 题目内容
时间:1s 空间:256M
题目描述:
“喂,CCF 么?”
“您好,这里是 CCF”
“哦,再见”
“…”
学军中学小帅给 CCF 打了一通奇怪的电话。因为他发现了前三分钟的电话费太便宜了,于是他准备用完三分钟挂电话,接着打。
CCF 一脸懵逼。。。
打完电话,小帅回到了学军机房,他开始思考刚才碰到的算法问题。
假如通话时间小于等于
3
3
3 分钟话费都是
b
a
s
e
base
base 块钱,大于三分钟以后每分钟按
a
b
o
v
e
above
above 块钱算,你有
t
o
t
a
l
total
total 块钱。问你最长能打多久电话。
输入格式:
输入一行,包含三个整数
b
a
s
e
,
a
b
o
v
e
,
t
o
t
a
l
base,above,total
base,above,total
输出格式:
输出一行,包含一个整数
样例输入:
2
2
2
1
1
1
4
4
4
样例输出:
6
6
6
约定:
1
≤
b
a
s
e
,
a
b
o
v
e
≤
100
,
1
≤
t
o
t
a
l
≤
10000
1 \leq base,above \leq 100,1 \leq total \leq 10000
1≤base,above≤100,1≤total≤10000
二 新知识点
2.1 while循环
现有一个正整数 a a a。你想要求出 a 3 \frac{a}{3} 3a 向上取整的结果(即第一个大于等于 a 3 \frac{a}{3} 3a 的数的大小),且不准使用内置函数,该怎么做呢?我们考虑不停地把 a a a 减去 3 3 3,直到 a ≤ 0 a \leq 0 a≤0 为止。要实现这一功能,就需要用到 while 循环,它可以在满足一定条件时自动退出循环,格式如下:
while(不退出循环的条件){ //注意,是“不退出循环”的条件!
语句
}
使用 while 循环,我们可以编写出死循环(即不会停止的循环):
while(1){ //即while(true)
语句
}
这时,只能按 Ctrl+C 组合键强制退出程序。
三 思路
比较单位时间(这里我取的是一分钟)内
b
a
s
e
base
base 和
a
b
o
v
e
above
above 的价格,再进行比较,决定使用下面的哪种方案:
(1)单位时间内
a
b
o
v
e
above
above 更贵,则不停地打三分钟的电话,挂断,再打,挂断,再打,
…
\ldots
…
…
\ldots
…
(2)单位时间内一样贵或
b
a
s
e
base
base 更贵,就只打一次电话,一直打到底。
但是,如果小帅连
b
a
s
e
base
base 元钱都付不起,那他只能喝西北风不打电话了。
四 AC代码
#include<bits/stdc++.h>
using namespace std;
int main(){
int base,above,total,s,minute=0;
double value1,value2;
cin>>base>>above>>total;
if(total<base){ //特判没钱的情况
cout<<0;
}else{
//计算价值
value1=1.0*base/3;
value2=1.0*above;
s=total; //让变量名好写一点,其实没必要
if(value1<value2){ //判断价值大小
while(s>0){ //尽量使用base
s-=base;
minute+=3;
}
if(s<0){ //处理冗余
s+=base;
minute-=3;
}
while(s>0){ //base不够,above来凑
s-=above;
minute++;
}
if(s<0){ //处理冗余
s+=above;
minute--;
}
}else{
s-=base;
minute+=3;
while(s>0){ //一打到底!
s-=above;
minute++;
}
if(s<0){ //处理冗余
s+=above;
minute--;
}
}
cout<<minute;
}
return 0;
}
Problem 9302 自动找人系统
一 题目内容
【题目描述】
给定三个参数
a
,
b
,
c
a,b,c
a,b,c,表示年,月,日,(不考虑闰年)表示一个时间节点。他要找出在两个时间节点之间的时间(包括两个时间节点本身)。现在给定两个时间节点和一个时间,他要判断这个时间是否在这个时间段之中。
【数据格式】
输入三行,三个时间,每行有三个数,表示年月日。前两行表示两个时间节点(不一定按照第一个小于第二个的顺序给出),最后一行是这个时间发生的时间。保证合法。
输出一个 Yes
或者 No
表示是否在这个时间段之中。
样例输入:
2007
2007
2007
1
1
1
1
1
1
2007
2007
2007
12
12
12
31
31
31
2007
2007
2007
3
3
3
22
22
22
样例输出:
Y
e
s
Yes
Yes
二 新知识点
2.1 long long类型
我们知道,int 类型的存储范围是 [ − 2 31 , 2 31 − 1 ] [-2^{31},2^{31}-1] [−231,231−1],即 [ − 2147483648 , 2147483647 ] [-2147483648,2147483647] [−2147483648,2147483647]。如何存储更大的数呢?可以用一种类似于 int 类型的整数类型——long long 类型。其存储范围是 [ − 2 63 , 2 63 − 1 ] [-2^{63},2^{63}-1] [−263,263−1],即 [ − 9223372036854775808 , 9223372036854775807 ] [-9223372036854775808,9223372036854775807] [−9223372036854775808,9223372036854775807],达到了 9 × 1 0 18 9 \times 10^{18} 9×1018 以上的数量级。其运算操作与 int 类型基本相同。
2.2 n n n进制
我们平常使用的数都是十进制的,如数
12345
12345
12345。这种数的进位规则是“逢十进一”,即某一位上的数一旦
≥
10
\geq 10
≥10,就向高位进
1
1
1。但
n
n
n 进制数的进位规则是“逢
n
n
n 进一”,即某一位上的数一旦
≥
n
\geq n
≥n,就向高位进
1
1
1。
n
n
n 进制数
a
a
a 记作
(
a
)
n
(a)_n
(a)n。
根据位值原理,设
(
a
)
n
(a)_n
(a)n 有
x
x
x 位,并记
(
a
)
n
(a)_n
(a)n 从低到高的第
k
k
k 位为
k
(
a
)
n
^k(a)_n
k(a)n(虽然这种记法很不规范,但有助于理解),其中
k
∈
[
1
,
n
]
∪
Z
k \in [1,n] \cup \mathbb{Z}
k∈[1,n]∪Z,则
(
a
)
n
=
∑
i
=
1
n
i
(
a
)
n
×
n
i
−
1
(a)_n=\sum_{i=1}^n {}^i(a)_n \times n^{i-1}
(a)n=i=1∑ni(a)n×ni−1,其中右边的式子的结果是
(
a
)
n
(a)_n
(a)n 的十进制表示。
三 思路
如果分每一种情况来看,虽然可以
A
C
\color{red}AC
AC,但代码太繁琐,因此考虑换一种思路。
把三个日期看作未完全进位的
32
32
32 进制数(因为月份和日期均不超过
31
31
31,但不知道年份的范围,因此直接乘以
3
2
2
=
1024
32^2=1024
322=1024,对结果没有影响),将其转化为十进制数,但怕年份取到一个很刁钻的数(如
1
0
9
10^9
109),于是开 long long 存储
32
32
32 进制数转化后的结果。这样,只要确认第三个转化后的数是否在前两个转化后的数之间即可(注意到 swap(a,b) 可用于任何数据类型,所以在必要时只需用 swap 交换两数即可)。
四 AC代码
#include<bits/stdc++.h>
using namespace std;
int main(){
long long a1,b1,c1,a2,b2,c2,a3,b3,c3; //存储三个日期
cin>>a1>>b1>>c1>>a2>>b2>>c2>>a3>>b3>>c3;
long long r1=a1*1024+b1*32+c1,r2=a2*1024+b2*32+c2,r3=a3*1024+b3*32+c3; //32进制转十进制
if(r1>r2)swap(r1,r2);
if(r3>=r1&&r3<=r2){
cout<<"Yes";
}else{
cout<<"No";
}
return 0;
}
Problem 3904 年龄计算
一 题目内容
时间:1s 空间:256M
题目描述
编写程序,输入某人的生日(年、月、日),并输入当前的日期(年、月、日),输出他的实际年龄(周岁),若生日超过当前日期,输出“-1”。
输入格式
6个整数,分别表示该学生的生日(年、月、日)和当前的日期(年、月、日)
输出格式
一个整数.
样例输入
1990 2 28
2000 3 4
样例输出
10
约定
保证输入数据是合法的日期
二 新知识点
本题没有新知识点。
三 思路
依据每种情况判断即可(本题这样做代码不繁琐)。
四 AC代码
#include<iostream>
using namespace std;
int main(){
int a1,b1,c1,a2,b2,c2; //存储两个日期
cin>>a1>>b1>>c1>>a2>>b2>>c2;
if(a1>a2){ //生日年份大于现在年份
cout<<-1;
}else if(a1==a2){ //生日年份等于现在年份
if(b1>b2){
cout<<-1;
}else if(b1==b2){
if(c1>c2){
cout<<-1;
}else if(c1==c2){
cout<<0;
}else if(c1<c2){
cout<<0;
}
}else if(b1<b2){
cout<<0;
}
}else if(a1<a2){ //生日年分小于现在年份
int y=a2-a1;
if(b1==b2){
if(c1<c2){
cout<<y;
}else if(c1==c2){
cout<<y;
}else if(c1>c2){
cout<<y-1;
}
}else if(b1<b2){
cout<<y;
}else if(b1>b2){
cout<<y-1;
}
}
return 0;
}