笔者在复习考研,今天无意中看到了南理工2019考研机试题,其中第一题是:已知2000年1月1日是星期六,给定天数n,问n天后是几几年几月几日星期几.按南理工往年命题风格,机试第一题通常是水题或签到题,但令人意外的是今年第一题比较难,难在细节处理繁琐复杂,思考问题时容易迷失方向,即便找到解题方法,编码时也容易出错。
这个问题笔者看到后花费1小时40分钟才完成思考和编码,这不包括调试的时间,南理工机试考试时间也就2个半小时,笔者承认,如果笔者自己上考场,第一题绝对做不出来或做错,不得不承认今年机试题难度有点大
现在给出解答,以下代码涉及5个函数
1:getDaysOfMonth 该函数接受年year和月month,返回month 月的天数
2:getDaysOfYear 该函数接受年year,返回该年的天数
3:calculateYearAndSurplusNumberOfDays 该函数接受年year和相对于该年一月一日的正偏移天数num_of_days,返回从年year一月一日算起num_of_days天后的日期date所在的年份(返回的pair的first成员)和date日期相对于该年份一月一日的正偏移天数(返回的pair的second成员)
4:calculateMonthAndSurplusNumberOfDays 该函数接受年year,月month,和相对于year年month月一号的正偏移天数num_of_days,返回从年year,month月一号算起num_of_days天后的日期date所在year年内的月份(返回的tuple的第二成员)以及date相对于该月份一号的正偏移天数(返回的tuple的第三成员),同时返回true(返回的tuple的第一成员).如果从年year,month月一号算起num_of_days天后的日期date所在年份超出year,则返回下一年year+1(返回的tuple的第二成员),和date超出year的正偏移天数(返回的tuple的第三成员),同时返回false(返回的tuple的第一成员)
5:calculateDayInMonth 该函数接受年year, 月month,日day和相对于year年, month月,day日的正偏移天数num_of_days,如果从年year, 月month,日day算起num_of_days后的日期date仍在月month内,返回date在month内的日数(返回的pair的second成员)和true(返回的pair的first成员)。否则返回date相对于month的下一个月一号的正偏移天数(返回的pair的second成员)和false(返回的pair的first成员)
主函数的执行逻辑是若已知year年,month月,day日星期数是week_count,偏移天数after_num_of_days
先调用calculateDayInMonth函数,若返回值包含true,则从year年,month月,day日算起after_num_of_days后的日期date年数就为year,月数为month,返回的天数就是month下的天数
否则令newyear为month月后一月所在年,new_month为month后一月,以new_year,new_month,和date相对于newmonth一号的偏移天数调用函数calculateMonthAndSurplusNumberOfDays,若返回值包含true,则date年数即为new_year,返回的月数即为date的月数
再以new_year,date的月数和date相对于该月数的偏移天数调用calculateDayInMonth算得date所在的天数
否则以calculateMonthAndSurplusNumberOfDays返回的年数和date相对于该年的偏移天数调用calculateYearAndSurplusNumberOfDays得到date所在年数year和date相对于year一月一号的偏移天数,该偏移天数小于等于year的总天数,再用year和date相对于year一月一号的偏移天数调用以及1月调用calculateMonthAndSurplusNumberOfDays得到date所在月数month和相对于month月一号的月内偏移天数(它小于等于month的总天数)。最后用year,month,一号和date相对于month月一号的月内偏移天数调用calculateDayInMonth算出date的天数day
最后处理的是星期数,如果题目所给星期数是星期天,则week_count为0,如果是星期一到星期六则week_count为对应数字,然后将week_count加上after_num_of_days,求出相加后得到的数除以7的余数,将余数简单转换即得星期数
c++代码如下:
#include <utility>
#include <tuple>
#include <iostream>
using namespace std;
int getDaysOfMonth(long long year, int month)
{
if (month == 2)
{
if (year % 400 == 0 || year % 4 == 0 && year % 100 != 0)
{
return 29;
}
else
{
return 28;
}
}
if (month == 4 || month == 6 || month == 9 || month == 11)
return 30;
else
return 31;
}
int getDaysOfYear(long long year)
{
if (year % 400 == 0 || year % 4 == 0 && year % 100 != 0)
return 366;
else
return 365;
}
pair<long long, long long> calculateYearAndSurplusNumberOfDays(long long year, long long num_of_days)
{
for (long long y = year; ; ++y)
{
int days = getDaysOfYear(year);
if (num_of_days <= days)
{
return { y, num_of_days };
}
num_of_days = num_of_days - days;
}
}
/*pair<long long, long long> calculateYearAndSurplusNumberOfDays(long long year, long long num_of_days)
{
long long days_offset = 1;
for (long long y = year; ; ++y)
{
int days = getDaysOfYear(year);
if (num_of_days < days_offset + days)
{
return { y, num_of_days - days_offset + 1 };
}
days_offset = days_offset + days;
}
}
这里是num_of_days的第二种实现方法*/
tuple<bool, long long, long long> calculateMonthAndSurplusNumberOfDays(long long year, int month, long long num_of_days)
{
int days_offset = 1;
for (int i = month; i <= 12; ++i)
{
int days = getDaysOfMonth(year, i);
if (num_of_days < days_offset + days)
{
return { true, i, num_of_days - days_offset + 1 };
}
days_offset = days_offset + days;
}
return { false, year + 1, num_of_days - days_offset + 1 };
}
pair<bool, long long> calculateDayInMonth(long long year, int month, int day, long long num_of_days)
{
int days = getDaysOfMonth(year, month);
if (num_of_days > days - day)
{
return { false, num_of_days - (days - day) };
}
else
{
return { true, day + num_of_days };
}
}
int main()
{
int year = 2000;
int month = 1;
int day = 1;
short week_count = 6;
long long after_num_of_days = 1000;
auto temp = calculateDayInMonth(year, month, day, after_num_of_days);
if (temp.first)
{
cout << year << "年" << month << "月" << temp.second << "日" << endl;
}
else
{
int new_year;
int new_month;
if (month == 12)
{
new_year = year + 1;
new_month = 1;
}
else
{
new_year = year;
new_month = month + 1;
}
auto p = calculateMonthAndSurplusNumberOfDays(new_year, new_month, temp.second);
if (get<0>(p))
{
cout << new_year << "年" << get<1>(p) << "月" << calculateDayInMonth(new_year, get<1>(p), 1, get<2>(p) - 1).second << "日";
}
else
{
auto s = calculateYearAndSurplusNumberOfDays(get<1>(p), get<2>(p));
auto v = calculateMonthAndSurplusNumberOfDays(s.first, 1, s.second);
cout << s.first << "年" << get<1>(v) << "月" << calculateDayInMonth(s.first, get<1>(v), 1, get<2>(v) - 1).second << "日";
}
}
week_count = (week_count + after_num_of_days) % 7;
if (week_count == 0)
{
cout << "星期天" << endl;
}
else
{
cout << "星期" << week_count << endl;
}
}
PS:这里简单讨论一下与上题无关的2019南理工机试第6题。即若有n个商品,编号1,2,—n,第i个商品数量为Ni,购买所需积分为Si,Ni和Si均为正数,问若已有积分C,则用积分C能买到的商品的积分总和最大是多少?
这题就是背包问题的变形,DP动态规划即可,设a[i,j]表示用积分j购买第1,2,–,i个物品时购买的物品的最大总积分,那么显然有状态转移方程a[i, j] = max{a[i-1, j], max{a[i-1, j-xSi]+xSi 1<= x <= Ni j-x*Si>=0}} 写出状态转移方程后编码就很容易了,最后代码也很简单,这里就不贴了