XJOI一级六段题解(g++,即C++),也可视作C++算法竞赛教程

这篇博客详细介绍了C++在算法竞赛中的应用,包括一维数组的使用、while循环、long long类型以及n进制等基础知识。通过五个具体的竞赛题目,展示了如何利用这些知识解决实际问题,如计算某月天数、博弈策略分析、硬币翻转、电话费用计算以及日期比较等。每个问题都提供了完整的AC代码,适合学习C++算法的初学者参考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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 个数据时,我们可以使用 a0a1a2a3 四个变量进行记录。但如果数据大小开到 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 09
(2)输入数组中的 n n n 个数(下标为 0 ∼ n − 1 0 \sim n-1 0n1):

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,y100000
输出 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 1base,above100,1total10000

二 新知识点

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 a0 为止。要实现这一功能,就需要用到 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,2311],即 [ − 2147483648 , 2147483647 ] [-2147483648,2147483647] [2147483648,2147483647]。如何存储更大的数呢?可以用一种类似于 int 类型的整数类型——long long 类型。其存储范围是 [ − 2 63 , 2 63 − 1 ] [-2^{63},2^{63}-1] [263,2631],即 [ − 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=1ni(a)n×ni1,其中右边的式子的结果是 ( 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;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值