
🫧个人主页:小年糕是糕手
💫个人专栏:《C++》《C++同步练习》《数据结构》《C语言》
🎨你不能左右天气,但你可以改变心情;你不能改变过去,但你可以决定未来!

目录
[思路1:通过静态成员变量统计累加结果 + 变长数组触发多次类构造,替代循环与条件判断,满足题目禁用关键字的要求。](#思路1:通过静态成员变量统计累加结果 + 变长数组触发多次类构造,替代循环与条件判断,满足题目禁用关键字的要求。)
[组件 1:辅助累加类 Sum------ 真正实现 "累加" 的工具](#组件 1:辅助累加类 Sum—— 真正实现 “累加” 的工具)
[组件 2:静态成员初始化 ------ 必须在类外执行](#组件 2:静态成员初始化 —— 必须在类外执行)
[组件 3:求解类 Solution------ 控制求和范围的 "总指挥"](#组件 3:求解类 Solution—— 控制求和范围的 “总指挥”)
[1.3、完整执行流程(以 n=3 为例,求 1+2+3)](#1.3、完整执行流程(以 n=3 为例,求 1+2+3))
[步骤 1:调用求和接口 Sum_Solution(3)](#步骤 1:调用求和接口 Sum_Solution(3))
[步骤 2:创建第一个对象 arr[0]------ 触发第一次构造函数](#步骤 2:创建第一个对象 arr[0]—— 触发第一次构造函数)
[步骤 3:创建第二个对象 arr[1]------ 触发第二次构造函数](#步骤 3:创建第二个对象 arr[1]—— 触发第二次构造函数)
[步骤 4:创建第三个对象 arr[2]------ 触发第三次构造函数](#步骤 4:创建第三个对象 arr[2]—— 触发第三次构造函数)
[步骤 5:返回结果](#步骤 5:返回结果)
思路:想想我们之前写得日期类,主要就是判断一个月的天数(注意闰年的2月),然后用我们的天数n去依次减去对应月份天数,当n小于对应月份天数时,找对对应月份,最后打印出来即可
[思路:将待累加的天数直接加到日期的 "日" 字段上,若 "日" 超过当月总天数,则不断减去当月天数、月份加 1(月份超 12 则年份加 1、月份重置为 1),直到 "日" 落在当月合法范围内。](#思路:将待累加的天数直接加到日期的 “日” 字段上,若 “日” 超过当月总天数,则不断减去当月天数、月份加 1(月份超 12 则年份加 1、月份重置为 1),直到 “日” 落在当月合法范围内。)
思路1:这也是一个日期类的简单应用,我们只需要找到每个月的天数,然后使用一个累加函数,不断加日期,如果日期<当月天数直接+上去即可
思路2:通过枚举每个月的1号是这一年的第几天,从而进行累加求和即可,其中注意闰年的处理
思路1:我们先看看俩个日期是否连续,连续的我们直接返回2即可,如果不是连续,我们分别计算这俩个日期与1年1月1日的差值,然后对应相减取绝对值即可
思路2:我们也可以用上一题所说的暴力破解方法的基础上来简化代码
一、求1+2+3+...+n
思路1:通过静态成员变量统计累加结果 + 变长数组触发多次类构造,替代循环与条件判断,满足题目禁用关键字的要求。
cpp
class Sum
{
public:
Sum()
{
_ret += _i;
++_i;
}
static int GetRet()
{
return _ret;
}
private:
static int _i;
static int _ret;
};
int Sum::_i = 1;
int Sum::_ret = 0;
//C++规定对象实例化必须调用构造
class Solution {
public:
//成员函数
int Sum_Solution(int n) {
// 变⻓数组
Sum arr[n];
return Sum::GetRet();
}
};
1.1、先明确代码整体结构
这段代码由 3 个核心部分 组成,分工明确:
cpp
// 1. 辅助累加类:负责实现「每次创建对象就累加一个数」的逻辑
class Sum { ... };
// 2. 静态成员初始化:静态变量必须在类外初始化(不属于任何对象)
int Sum::_i = 1;
int Sum::_ret = 0;
// 3. 求解类:提供对外的求和接口,控制累加的次数(即 1~n 的范围)
class Solution {
public:
int Sum_Solution(int n) { ... } // 求和核心接口
};
1.2、逐个拆解核心组件(重点!)
组件 1:辅助累加类 Sum------ 真正实现 "累加" 的工具
Sum 类的作用是「通过构造函数自动累加」,每个成员都有明确分工
cpp
class Sum
{
public:
// (1)构造函数:名字和类名相同,无返回值,创建对象时自动调用
Sum()
{
_ret += _i; // 核心操作1:把当前要加的数(_i)累加到总和(_ret)
++_i; // 核心操作2:让下一个要加的数自增1(比如这次加1,下次加2)
}
// (2)静态成员函数:获取累加结果的"接口"
static int GetRet()
{
return _ret; // 返回已经累加好的总和
}
private:
// (3)静态成员变量:所有对象共享,生命周期和程序一致(存在静态区)
static int _i; // 记录「当前要累加的数字」,初始值1(从1开始加)
static int _ret; // 记录「累加的总和」,初始值0(一开始总和为0)
};
关键说明(为什么用「静态成员」?)
- 若
_i和_ret是普通成员变量:每个Sum对象会有独立的_i和_ret副本(比如对象 1 的_i=1,对象 2 的_i也是 1),无法实现 "1→2→3→...→n" 的连续累加;- 若设为
static静态成员:所有Sum对象共享同一个_i和_ret(比如对象 1 改了_i=2,对象 2 用的就是 2),才能实现 "累加传递"。
组件 2:静态成员初始化 ------ 必须在类外执行
cpp
int Sum::_i = 1; // 初始化「当前要累加的数」为1(从1开始求和)
int Sum::_ret = 0; // 初始化「累加总和」为0(初始无任何累加)
- 为什么要在类外初始化?静态成员变量不属于任何对象,不会走类的构造函数初始化列表,必须在类外单独分配内存并赋值(否则编译报错)。
组件 3:求解类 Solution------ 控制求和范围的 "总指挥"
cpp
class Solution {
public:
int Sum_Solution(int n) { // n是求和上限(比如n=5就是求1+2+3+4+5)
Sum arr[n]; // 核心操作:创建一个长度为n的Sum类型数组(变长数组)
return Sum::GetRet(); // 返回累加好的总和
}
};
关键说明(
Sum arr[n]到底做了什么?)
- 这是「变长数组」(GCC 编译器扩展特性),作用是「创建 n 个 Sum 类的对象」;
- 数组的每个元素都是一个
Sum对象,创建数组时会依次创建这 n 个对象;- 每创建一个
Sum对象,就会自动调用一次Sum类的构造函数(也就是执行_ret += _i; ++_i;);- 相当于通过 "创建 n 个对象",间接实现了 "循环 n 次累加"(替代了 for/while 循环,满足题目禁用关键字的要求)。
1.3、完整执行流程(以 n=3 为例,求 1+2+3)
我们模拟调用 Solution().Sum_Solution(3) 后的每一步,看代码如何一步步算出结果:
步骤 1:调用求和接口 Sum_Solution(3)
- 传入参数
n=3,表示要计算 1+2+3;- 执行
Sum arr[3]:创建长度为 3 的数组,需要依次创建 3 个Sum对象(arr [0]、arr [1]、arr [2])。
步骤 2:创建第一个对象 arr[0]------ 触发第一次构造函数
- 构造函数执行:
_ret += _i→_ret = 0 + 1 = 1(总和现在是 1);++_i→_i = 1 + 1 = 2(下一个要加的数是 2);- 此时状态:
_ret=1,_i=2。
步骤 3:创建第二个对象 arr[1]------ 触发第二次构造函数
- 构造函数执行:
_ret += _i→_ret = 1 + 2 = 3(总和现在是 3);++_i→_i = 2 + 1 = 3(下一个要加的数是 3);- 此时状态:
_ret=3,_i=3。
步骤 4:创建第三个对象 arr[2]------ 触发第三次构造函数
- 构造函数执行:
_ret += _i→_ret = 3 + 3 = 6(总和现在是 6);++_i→_i = 3 + 1 = 4(下一个要加的数是 4,但已经用不到了);- 此时状态:
_ret=6,_i=4。
步骤 5:返回结果
- 调用
Sum::GetRet(),获取静态成员_ret的值(6);Sum_Solution(3)函数返回 6,即 1+2+3 的结果。
代码改造:采用内部类
cpp
class Solution {
// 内部类
class Sum {
public:
Sum() {
_ret += _i;
++_i;
}
};
static int _i;
static int _ret;
public:
int Sum_Solution(int n) {
// 变⻓数组
Sum arr[n];
return _ret;
}
};
int Solution::_i = 1;
int Solution::_ret = 0;
二、打印日期
思路:想想我们之前写得日期类,主要就是判断一个月的天数(注意闰年的2月),然后用我们的天数n去依次减去对应月份天数,当n小于对应月份天数时,找对对应月份,最后打印出来即可
cpp
#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
,_month(month)
,_day(day)
{ }
// 得到当月天数(处理闰年二月)
int GetMonthDay(int year, int month)
{
assert(month > 0 && month < 13);
// 月份天数数组(下标0不用,下标1对应1月)
static int monthDayArray[13] = { -1, 31,28,31,30,31,30,31,31,30,31,30,31 };
// 闰年二月特殊处理
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
return 29;
}
else
{
return monthDayArray[month];
}
}
//检查日期是否合法
bool CheckDate()
{
if (_month < 1 || _month>12)
return false;
if (_day<1 || _day>GetMonthDay(_year, _month))
return false;
return true;
}
// 根据"年+第n天"计算对应的月和日
void SetDateByDay(int year, int n)
{
_year = year;
_month = 1;
// 循环减去每个月的天数,直到n小于当月天数
while (n > GetMonthDay(_year, _month))
{
n = n - GetMonthDay(_year, _month);
_month++;
}
_day = n;
}
// 格式化输出日期(yyyy-mm-dd)
void Print()
{
// 输出年份(直接输出,年份是4位不用补0)
cout << _year << "-";
// 月份:小于10则补0,否则直接输出
if (_month < 10)
{
cout << "0" << _month;
}
else
{
cout << _month;
}
cout << "-";
// 日期:小于10则补0,否则直接输出
if (_day < 10)
{
cout << "0" << _day;
}
else
{
cout << _day;
}
// 换行
cout << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int year, n;
// 多组输入:年 + 第n天
while (cin >> year >> n) {
Date d;
d.SetDateByDay(year, n); // 计算对应日期
d.Print(); // 格式化输出
}
return 0;
}
三、日期累加
思路:将待累加的天数直接加到日期的 "日" 字段上,若 "日" 超过当月总天数,则不断减去当月天数、月份加 1(月份超 12 则年份加 1、月份重置为 1),直到 "日" 落在当月合法范围内。
cpp
//累加天数
#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{ }
//得到每个月的天数
int GetMonthDay(int year, int month)
{
assert(month >= 1 && month <= 12);
int arr[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
return 29;
}
else
{
return arr[month];
}
}
//检查日期是否合法
bool CheckDate()
{
if (_month >= 13 || _month < 1)
{
return false;
}
if (_day < 1 || _day > GetMonthDay(_year, _month))
{
return false;
}
return true;
}
// 日期累加days天
void AddDays(int days)
{
_day += days;
while (true)
{
if (_day <= GetMonthDay(_year, _month))
{
break; // 天数在当月范围内,结束
}
// 天数超过当月,减去当月天数,月份+1
_day -= GetMonthDay(_year, _month);
_month++;
if (_month > 12) { // 月份超过12,年份+1,月份重置为1
_year++;
_month = 1;
}
}
}
//格式化输出
void Print()
{
cout << _year << "-";
if (_month < 10)
{
cout << "0" << _month;
}
else
{
cout << _month;
}
cout << "-";
if (_day < 10)
{
cout << "0" << _day;
}
else
{
cout << _day;
}
cout << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int m;
cin >> m;
while (m--)
{
int year, month, day, AddDay;
cin >> year >> month >> day >> AddDay;
Date d(year, month, day);
d.AddDays(AddDay);
d.Print();
}
return 0;
}
四、计算日期到天数转换
思路1:这也是一个日期类的简单应用,我们只需要找到每个月的天数,然后使用一个累加函数,不断加日期,如果日期<当月天数直接+上去即可
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{ }
//得到每个月的天数
int GetMonthDay(int year, int month)
{
assert(month >= 1 && month <= 12);
int arr[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
return 29;
}
else
{
return arr[month];
}
}
//判断日期是否在正确区间
bool CheckDate()
{
if (_month > 12 || _month < 1)
return false;
if (_day<1 || _day>GetMonthDay(_year, _month))
return false;
return true;
}
//日期加法
int add(int sum)
{
sum = _day; // 先算当月的天数(核心:先加_day,再累加之前的月份)
_month--; // 从上个月开始累加(因为当月的day已经算过了)
while (_month > 0) // 只要月份还大于0,就继续累加
{
sum += GetMonthDay(_year, _month); // 累加前一个月的天数
_month--; // 月份继续减,直到1月
}
return sum; // 最终返回的就是"当年第几天
}
void Print()
{
cout << add(0) << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
int year, month, day;
while (cin >> year >> month >> day)
{
//别忘记传值,不然会调用默认构造
Date d(year, month, day);
d.Print();
}
return 0;
}
当然这里的add是有优化空间的,我们可以创建一个临时变量就可以保留下_month的值,方便后续的二次使用(本题没有二次使用,所以使用哪个都可以):
cpp
//日期加法
int add(int sum)
{
int tmp_month = _month;
sum = _day;
tmp_month--;
while (tmp_month > 0)
{
sum += GetMonthDay(_year, tmp_month);
tmp_month--;
}
return sum;
}
这题还可以使用枚举是个非常暴力破解的方法:
思路2:通过枚举每个月的1号是这一年的第几天,从而进行累加求和即可,其中注意闰年的处理
cpp
#include<iostream>
using namespace std;
int main()
{
int year, month, day;
while (cin >> year >> month >> day)
{
int monthDays[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
int nday = monthDays[month - 1] + day;
if (month > 2 && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0))
{
nday += 1;
}
cout << nday << endl;
}
return 0;
}
五、日期差值
思路1:我们先看看俩个日期是否连续,连续的我们直接返回2即可,如果不是连续,我们分别计算这俩个日期与1年1月1日的差值,然后对应相减取绝对值即可
cpp
#define _CRT_SECURE_NO_WARNINGS 1
//有两个日期,求两个日期之间的天数,如果两个日期是连续的我们规定他们之间的天数为两天
//有多组数据,每组数据有两行,分别表示两个日期,形式为YYYYMMDD
//每组数据输出一行,即日期差值
#include<iostream>
#include<assert.h>
#include<string>
#include<cmath> // 用于abs()取绝对值
using namespace std;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{
}
// 判断是否为闰年
bool isLeap() const {
return ((_year % 4 == 0 && _year % 100 != 0) || (_year % 400 == 0));
}
// 获取当月天数
int getMonthDays() const {
int days[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
if (_month == 2 && isLeap()) {
return 29;
}
return days[_month];
}
// 计算当前日期距离基准年(1年)的总天数
long long getTotalDays() const {
long long total = 0;
// 累加之前所有年份的天数
for (int y = 1; y < _year; ++y) {
total += ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0)) ? 366 : 365;
}
// 累加当年的天数
for (int m = 1; m < _month; ++m) {
int d[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
if (m == 2 && isLeap()) {
total += 29;
}
else {
total += d[m];
}
}
total += _day;
return total;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
string dateStr1;
string dateStr2;
while (cin >> dateStr1 >> dateStr2) // 读取每一行的日期字符串(比如"20110412")
{
//这里是连续输入字符串我们需要调用string类中的substr来辅助我们实现
//string的substr是C++中用于截取字符串子串的成员函数,作用是从原字符串中提取一部分内容,返回一个新的字符串
// 拆分年、月、日:前4位→年,中间2位→月,最后2位→日
int year1 = stoi(dateStr1.substr(0, 4)); // 从索引0开始取4个字符→年
int month1 = stoi(dateStr1.substr(4, 2)); // 从索引4开始取2个字符→月
int day1 = stoi(dateStr1.substr(6, 2)); // 从索引6开始取2个字符→日
int year2 = stoi(dateStr2.substr(0, 4)); // 从索引0开始取4个字符→年
int month2 = stoi(dateStr2.substr(4, 2)); // 从索引4开始取2个字符→月
int day2 = stoi(dateStr2.substr(6, 2)); // 从索引6开始取2个字符→日
//stoi是C++11引入的标准库函数
//全称是string to integer核心作用是把字符串类型的数字(如 "2011"、"04")转换成整数类型(int)
//是处理字符串数字拆分(比如日期拆分)的核心函数
Date d1(year1, month1, day1);
Date d2(year2, month2, day2);
// 计算差值(题目规则:连续日期计为2天,即差值+1)
long long diff = abs(d1.getTotalDays() - d2.getTotalDays()) + 1;
cout << diff << endl;
}
return 0;
}
思路2:我们也可以用上一题所说的暴力破解方法的基础上来简化代码
cpp
#include <iostream>
using namespace std;
//思路:1.分别求出每一个日期与0000年0月1日距离的天数
// 2.两个距离天数相减即可得到两个日期相差的天数
//平年从1月到n月的天数
int mon[12] = { 0,31,59,90,120,151,181,212,243,273,304,334 };
//给出年月日,计算距离0000年0月1日的天数和
int CountDay(int y, int m, int d)
{
// 计算0-y年的天数
int yearDay = y * 365 + y / 4 - y / 100 + y / 400;
// 计算到0-m月的天数
int monthDay = mon[m - 1];
if (m > 2 && ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0))
monthDay += 1;
return yearDay + monthDay + d;
}
int main()
{
int year1, month1, day1;
scanf("%4d%2d%2d", &year1, &month1, &day1);
int n1 = CountDay(year1, month1, day1);
int year2, month2, day2;
scanf("%4d%2d%2d", &year2, &month2, &day2);
int n2 = CountDay(year2, month2, day2);
cout << abs(n1 - n2) + 1 << endl;
}
核心优势
- 效率高:用数学公式替代循环,计算大年份(比如 10000 年)也瞬间出结果;
- 逻辑简洁:把 "闰年、月份天数" 的复杂规则浓缩成公式,代码量少;
- 适配题目规则:+1 操作直接满足 "连续日期计为 2 天" 的要求。
