C语言程序设计100例之(71):18岁生日
例71 18岁生日
问题描述
小明的18岁生日就要到了,他当然很开心,可是他突然想到一个问题,是不是每个人从出生开始,到达18岁生日时所经过的天数都是一样的呢?似乎并不全都是这样,所以他想请你帮忙计算一下他和他的几个朋友从出生到达18岁生日所经过的总天数,让他好来比较一下。
输入
一个数T,后面T行每行有一个日期,格式是YYYY-MM-DD。如我的生日是1988-03-07。
输出
T行,每行一个数,表示此人从出生到18岁生日所经过的天数。如果这个人没有18岁生日,就输出-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
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 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; } 问题描述 今天是2017年8月6日,农历闰六月十五。 小明独自凭栏,望着一轮圆月,发出了“今夕何夕,见此良人”的寂寞感慨。 为了排遣郁结,他决定思考一个数学问题:接下来最近的哪一年里的同一个日子,和今天的星期数一样?比如今天是8月6日,星期日。下一个也是星期日的8月6日发生在2023年。 输入 第一行为T(1 ≤ T ≤ 10000),表示输入数据组数。 每组数据包含一个日期,格式为YYYY-MM-DD。其中YYYY ≥ 2017,日期一定是个合法的日期。 输出 对每组数据输出答案年份,题目保证答案不会超过四位数。 输入样例 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 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; } 问题描述 给定一个表示起始年份的正整数Y和一个正整数N,你的任务是求出从Y年开始的第N个闰年。 注:如果Y年是闰年,那么第一个闰年就是Y年。 输入 输入包含几个测试用例。输入的第一行是一个整数T,它是测试用例的数量。 每个测试用例包含两个正整数Y和N(1<=N<=10000)。 输出 对于每个测试用例,您应该输出从Y年开始的第N个闰年。 输入样例 2005 25 1855 12 2004 10000 输出样例 2108 1904 43236 (1)编程思路。 编写函数int leapyear(int year)判断year年是否是闰年。为求从Y年开始的第N个闰年,进行简单的循环处理即可。 (2)源程序。 #include 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 { year+=4; if (leapyear(year)==1) cnt++; } printf("%d\n",year); } return 0; } 问题描述 在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。 小明习惯用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 输出样例 (1)编程思路。 采用字符串输入两个给定的日期date1和date2,然后分离出这两个日期的年份year1和year2。由于一年中最多只可能有一个回文日期,例如,若年份为abcd,则该年的回文日期可能为abcddcba,若dc在01~12之间,bc在01~XX(XX为dc月最多的天数),则abcddcba才是合法的回文日期。 因此,对year1和year2之间的年份进行穷举,对每个枚举到的年份,判断该年可能的回文日期是否合法,同时对于第year1年,可能的回文日期不能小于date1,第year2年,可能的回文日期不能大于date2,条件均满足,则是一个回文日期,计数。 (2)源程序。 #include 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 if (i==year2 && month*100+day>endmd) continue; cnt++; } printf("%d\n", cnt); return 0; }习题71
71-1 今夕何夕
71-2 第N个闰年
71-3 回文日期