C语言程序设计100例之(71):18岁生日
例71 18岁生日
问题描述
小明的18岁生日就要到了,他当然很开心,可是他突然想到一个问题,是不是每个人从出生开始,到达18岁生日时所经过的天数都是一样的呢?似乎并不全都是这样,所以他想请你帮忙计算一下他和他的几个朋友从出生到达18岁生日所经过的总天数,让他好来比较一下。
输入
一个数T,后面T行每行有一个日期,格式是YYYY-MM-DD。如我的生日是1988-03-07。
输出
T行,每行一个数,表示此人从出生到18岁生日所经过的天数。如果这个人没有18岁生日,就输出-1。
输入样例
1
1988-03-07
输出样例
6574
(1)编程思路。
设输入的出生日期为year年month月day日,则18岁生日是year+18年month月day日,若year+18年不是闰年,但month=2,day=29,则18岁生日不存在,输出-1。
设所经过的天数为sum,初始时sum=0。每年至少有365天,18年,所以sum=sum+18*365;闰年有365天,因此在第year+1~year+17这17年中每个闰年加1天;若month>2,则第year+18年是闰年的话也要加1天;若month<2或者month==2但day!=29,则第year年是闰年的话也要加1天。将这些情况都考虑清楚不要遗漏。
(2)源程序。
#include <stdio.h>
int leapyear(int year)
{
if (year%4==0 && year%100!=0 || year%400==0)
return 1;
else
return 0;
}
int main()
{
int t,year,month,day,sum;
scanf("%d", &t);
while(t--)
{
scanf("%d-%d-%d", &year, &month, &day);
if (month == 2 && day == 29 && leapyear(year +18)!=1)
printf("-1\n");
else
{
sum = 365*18;
for (int i=year+1; i<year+18; i++)
sum += leapyear(i);
if (month > 2)
sum += leapyear(year + 18);
else if (month<2 || day != 29)
sum += leapyear(year);
printf("%d\n", sum);
}
}
return 0;
}
习题71
71-1 今夕何夕
问题描述
今天是2017年8月6日,农历闰六月十五。
小明独自凭栏,望着一轮圆月,发出了“今夕何夕,见此良人”的寂寞感慨。
为了排遣郁结,他决定思考一个数学问题:接下来最近的哪一年里的同一个日子,和今天的星期数一样?比如今天是8月6日,星期日。下一个也是星期日的8月6日发生在2023年。
输入
第一行为T(1 ≤ T ≤ 10000),表示输入数据组数。
每组数据包含一个日期,格式为YYYY-MM-DD。其中YYYY ≥ 2017,日期一定是个合法的日期。
输出
对每组数据输出答案年份,题目保证答案不会超过四位数。
输入样例
3
2017-08-06
2017-08-07
2018-01-01
输出样例
2023
2023
2024
(1)编程思路。
目前全世界通用的历法称为公历,它实质上是一种阳历。
原始的阳历是古埃及人创立的。最初取一年为365日。为了协调历法年与回归年的长度,公元前46年罗马统治者儒略·凯撒对阳历作了修改,制定儒略历。公元前8年,凯撒的侄儿奥古斯都又对儒略历作为调整。儒略历分一年为十二个月,平年365日;年份能被4整除的为闰年,共366日。这样,儒略历历年平均长度便是365.25日,同回归年长度365.2422日相差0.07078日,400年约差3日。从实施儒略历到十六世纪末期,累差约为10日。为了消除了这个差数,教皇格里高利十三世把儒略历1582年10月4日的下一天定为10月15日,中间消去10天;同时还修改了儒略历置闰法则:能被4除尽的年份仍然为闰年,但对世纪年(如1600,1700,……),只有能被400除尽的才为闰年。这样,400年中只有97个闰年,比原来减少三个,使历年平均长度为365.2425日,更接近于回归年的长度。经过这样修改的儒略历叫格里高利历,亦称格里历。格里历二十世纪初为全世界普遍采用,所以又叫公历。
如何确定一个给定的日期是星期几呢?
设变量year、month和day分别表示给定日期的年、月和日。由于一个星期有7天,因此,如果我们知道该年的元旦是星期几(设为week),那么该日期是星期几就可以确定了,计算公式为:(week+d-1)%7。0代表星期日,其中d为给定日期是当年的第几天。
怎样求年号为year这年的元旦是星期几呢?用一个简单公式即可计算出来。
设公元 1 年 1月 1日 为星期一。
我们知道一年有365天(当然闰年会有366天,先不管了),每7天1周,365%7=1。即若不考虑闰年,则每年的元旦是星期几应该是上一年元旦星期几的后一天。
由于 1 年元旦是星期一,因此 2年元旦星期二,3年元旦星期三,4年元旦星期四,…即
week =(year)%7。
由于闰年的存在会在2月多一天,因此,闰年的下一年元旦星期几应再加1,即5年为星期六(不是星期五,因为 4年是闰年),因此需要知道前year-1年中有多少个闰年。闰年的规则简述就是每4年一个闰年,每100年不是闰年,每400年又是闰年。按集合包含与容斥规则,闰年个数有 (year-1)/4 - (year-1)/100 + (year-1)/400。
因此,已知年号year,就可以根据year计算出该年元旦是星期几。计算公式如下:
week=[ year+(year-1)/4-(year-1)/100+(year-1)/400]%7;
需要说明的是,对于公元历任一年,这个公式不一定成立,因为由前面的介绍,公元历在历史上也不断调整过,但对于现行的公历,这一公式是可以正确确定某年元旦是星期几的。
例如,year=2001,week=(2001+(2001-1)/4-(2001-1)/100+(2001-1)/400)%7
=(2001+500-20+5)%7=1
即2001年元旦是星期一,正好符合。
确定了每年元旦是星期几后,对之后的年份进行穷举,找到一个最近的日期,其星期相同即可。不考虑闰年的话,若某两年的元旦星期相同,则两年相同的日子的星期也相同;若考虑闰年,则对2月份以后的日子进行闰年处理。
(2)源程序。
#include <stdio.h>
bool isLeap(int year)
{
if (year%4==0 && year%100!=0 || year%400==0)
return true;
else
return false;
}
int main()
{
int year,month,day,t,week1,week2,i;
scanf("%d",&t);
while(t--)
{
scanf("%d-%d-%d",&year,&month,&day);
week1=(year+(year-1)/4-(year-1)/100+(year-1)/400)%7;
for (i=year+1;i<=9999;i++)
{
if (month==2&&day==29&&!isLeap(i))
continue;
week2=(i+(i-1)/4-(i-1)/100+(i-1)/400)%7;
if(month>2)
{
if (isLeap(year) && ! isLeap(i))
week2=(week2-1+7)%7;
if (!isLeap(year) && isLeap(i))
week2=(week2+1)%7;
}
if (week1==week2)
{
printf("%d\n",i);
break;
}
}
}
return 0;
}
71-2 第N个闰年
问题描述
给定一个表示起始年份的正整数Y和一个正整数N,你的任务是求出从Y年开始的第N个闰年。
注:如果Y年是闰年,那么第一个闰年就是Y年。
输入
输入包含几个测试用例。输入的第一行是一个整数T,它是测试用例的数量。
每个测试用例包含两个正整数Y和N(1<=N<=10000)。
输出
对于每个测试用例,您应该输出从Y年开始的第N个闰年。
输入样例
3
2005 25
1855 12
2004 10000
输出样例
2108
1904
43236
(1)编程思路。
编写函数int leapyear(int year)判断year年是否是闰年。为求从Y年开始的第N个闰年,进行简单的循环处理即可。
(2)源程序。
#include <stdio.h>
int leapyear(int year)
{
if (year%4==0 && year%100!=0 || year%400==0)
return 1;
else
return 0;
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
int year,n;
scanf("%d%d",&year,&n);
while (leapyear(year)==0) year++;
int cnt=1;
while (cnt<n)
{
year+=4;
if (leapyear(year)==1) cnt++;
}
printf("%d\n",year);
}
return 0;
}
71-3 回文日期
问题描述
在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。
小明习惯用8位数字表示一个日期,其中,前4位代表年份,接下来2位代表月份,最后2位代表日期。显然:一个日期只有一种表示方法,而两个不同的日期的表示方法不会相同。
小明认为,一个日期是回文的,当且仅当表示这个日期的8位数字是回文的。现在,小明想知道:在他指定的两个日期之间包含这两个日期本身,有多少个真实存在的日期是回文的。
一个8位数字是回文的,当且仅当对于所有的i (1≤i≤8)从左向右数的第i个数字和第9−i个数字(即从右向左数的第i个数字)是相同的。
例如:
•对于2016年11月19日,用8位数字20161119表示,它不是回文的。
•对于2010年1月2日,用8位数字20100102表示,它是回文的。
•对于2010年10月2日,用8位数字20101002表示,它不是回文的。
每一年中都有12个月份:
其中,1,3,5,7,8,10,12月每个月有31天;4,6,9,11月每个月有30天;而对于2月,闰年时有29天,平年时有28天。
输入
两行,每行包括一个8位数字。
第一行表示小明指定的起始日期date 1。
第二行表示小明指定的终止日期date 2。
输入数据保证都是真实存在的日期,且年份部分一定为4位数字,且首位数字不为0。
保证date1 —定不晚于date2。
输出
一个整数,表示在date1和date2之间,有多少个日期是回文的。
输入样例
20000101
20101231
输出样例
2
(1)编程思路。
采用字符串输入两个给定的日期date1和date2,然后分离出这两个日期的年份year1和year2。由于一年中最多只可能有一个回文日期,例如,若年份为abcd,则该年的回文日期可能为abcddcba,若dc在01~12之间,bc在01~XX(XX为dc月最多的天数),则abcddcba才是合法的回文日期。
因此,对year1和year2之间的年份进行穷举,对每个枚举到的年份,判断该年可能的回文日期是否合法,同时对于第year1年,可能的回文日期不能小于date1,第year2年,可能的回文日期不能大于date2,条件均满足,则是一个回文日期,计数。
(2)源程序。
#include <stdio.h>
int main()
{
int table[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
char date1[9],date2[9];
scanf("%s%s",date1,date2);
int year1=0,year2=0,i;
int beginmd=0,endmd=0;
for (i=0;i<4;i++)
{
year1=year1*10+date1[i]-'0';
year2=year2*10+date2[i]-'0';
beginmd=beginmd*10+date1[i+4]-'0';
endmd=endmd*10+date2[i+4]-'0';
}
int cnt=0;
for (i=year1;i<=year2;i++)
{
if ((i%4==0 && i%100!=0) || i%400==0)
table[2]=29;
else
table[2]=28;
int month=(i%10)*10+(i/10)%10;
if (month<1 || month>12) continue;
int day=i/1000+((i/100)%10)*10;
if (day<1 || day>table[month]) continue;
if (i==year1 && month*100+day<beginmd) continue;
if (i==year2 && month*100+day>endmd) continue;
cnt++;
}
printf("%d\n", cnt);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)