SzNOI之d100题解题报名,日历问题
这题的题目有点古怪。解题过程,且慢慢道来。
搞懂这题,算日历,周几等问题,基本都搞清了。
从公元一年,到公元n年,共有几天?
这问题似乎相当简单,因为一年有365嘛。比如公元一年,到公元二年,这样算:
var y,day:longint; begin y:=2; // 10 day:=y*365; writeln(day); end.
算下来,似乎一点错误也木有。
那到公元十年呢?是不是把2改成10就可以了?
不可以,因为闰年有366天。要这样计算:
var i,y,day,d:longint; function leapy(y:longint):boolean; begin if(y mod 400=0) or (y mod 4=0) and (y mod 100<>0) then exit(true) else exit(false); end; begin y:=10; //2013 d:=0; for i:=1 to y do if(leapy(i)) then inc(d); day:=y*365+d; writeln(day); end.
是的,要把闰年多出来的一天,也加进去。
应该是3652天,而不是3650天。
分析一下,很容易明白,因为公元四年,与公元八年,是闰年。
把10,改成2013试试?
这样计算天数,并不是最快的。看程序:
var i,y,day,d,e:longint; function leapy(y:longint):boolean; begin if(y mod 400=0) or (y mod 4=0) and (y mod 100<>0) then exit(true) else exit(false); end; begin y:=2013; d:=0; for i:=1 to y do if(leapy(i)) then inc(d); day:=y*365+d; writeln(d); writeln(day); e:=(y div 4)-(y div 100)+(y div 400); writeln(e); end.
两种算法,得出的结论是一样的,都是488。
当然是第二种算法快得多,不用循环,直接算出来了。
我们可以粗略理解一下,整除4,闰年多了点。那么减去一些(整除100的结果),那又太少了一些,那么再加上一点。。
好了,以下的算法很快就能计算出,从公元一年到y年,共有几个闰年,共有几天:
var y,day,d:longint; begin readln(y); // y:=2013; d:=(y div 4)-(y div 100)+(y div 400); day:=y*365+d; writeln(d); writeln(day); end.
从公元一年的一月一日开始,到今天,共有几天呢?
要分三步走:
公元一年,到去年,共有几天。
今年一月,到这个月的前一个月,共有几天。
这个月的一号,到今天,共有几天。
const a:array[0..12] of longint = (0,31,28,31,30,31,30,31,31,30,31,30,31); var i,t,y,day,d,m,dd:longint; function leapy(y:longint):boolean; begin if(y mod 400=0) or (y mod 4=0) and (y mod 100<>0) then exit(true) else exit(false); end; begin y:=2013; //2014年的前一年,是2013年 m:=11; //当月是11月 dd:=1; //1号 d:=(y div 4)-(y div 100)+(y div 400); day:=y*365+d; t:=0; for i:=1 to m-1 do inc(t,a[i]); //1月到前一个月的天数 if( (m>2) and leapy(y) ) then inc(t); //闰年多加一天 inc(day,t); //到前个月底有几天 inc(day,dd); //到今天有几天 writeln(day); end.
那今天是周几?
公元一年的元旦的前一天,是周日。这个非常好记,因为传说上帝创世纪之后,是在周日休息的。
到了公元一年的第一天,出来干活了,是周一。
公元一年的元旦的前一天,我们暂时称之为公元前末日。
从公元前末日,到今天,相差几天,我们前面已经计算出来了。
而公元前末日,是周日,那今天是周几,就非常好算了,看程序:
const a:array[0..12] of longint = (0,31,28,31,30,31,30,31,31,30,31,30,31); var i,t,y,day,d,m,dd:longint; function leapy(y:longint):boolean; begin if(y mod 400=0) or (y mod 4=0) and (y mod 100<>0) then exit(true) else exit(false); end; begin y:=2013; //去年 m:=11; dd:=1; d:=(y div 4)-(y div 100)+(y div 400); day:=y*365+d; t:=0; for i:=1 to m-1 do inc(t,a[i]); if( (m>2) and leapy(y+1) ) then inc(t); //闰年多加一天 inc(day,t); //到前个月底有几天 inc(day,dd); //到今天有几天 writeln(day); case (day mod 7) of 0 : writeln('Sunday'); //公元前末日是周日 1 : writeln('Monday'); 2 : writeln('Tuesday'); 3 : writeln('Wednesday'); 4 : writeln('Thursday'); 5 : writeln('Friday'); 6 : writeln('Saturday'); end; end.
是周六,对吧?
那么稍微修改一下程序,对于任意年月日是周几,都可以准确、精确地算出来了:
const a:array[0..12] of longint = (0,31,28,31,30,31,30,31,31,30,31,30,31); var i,t,y,day,d,m,dd:longint; function leapy(y:longint):boolean; begin if(y mod 400=0) or (y mod 4=0) and (y mod 100<>0) then exit(true) else exit(false); end; begin readln(y,m,dd); y:=y-1; //年份算到去年 d:=(y div 4)-(y div 100)+(y div 400); day:=y*365+d; t:=0; for i:=1 to m-1 do inc(t,a[i]); if( (m>2) and leapy(y+1) ) then inc(t); //今年是y+1 inc(day,t); inc(day,dd); case (day mod 7) of 0 : writeln('Sunday'); 1 : writeln('Monday'); 2 : writeln('Tuesday'); 3 : writeln('Wednesday'); 4 : writeln('Thursday'); 5 : writeln('Friday'); 6 : writeln('Saturday'); end; end.
有了前面的铺垫,解题报告正式开始。
似乎、好像、仿佛,已经没有什么好说的了,直接看程序:
const a:array[0..12] of longint = (0,31,28,31,30,31,30,31,31,30,31,30,31); w:array[0..6] of string = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat'); var i,t,y,day,d,m,dd:longint; function leapy(y:longint):boolean; begin if(y mod 400=0) or (y mod 4=0) and (y mod 100<>0) then exit(true) else exit(false); end; begin readln(y,m); y:=y-1; //年份算到去年 dd:=1; d:=(y div 4)-(y div 100)+(y div 400); day:=y*365+d; t:=0; for i:=1 to m-1 do inc(t,a[i]); if( (m>2) and leapy(y+1) ) then inc(t); //今年是y+1 inc(day,t); inc(day,dd); // d:=day mod 7; //得出1号是周几 for i:=0 to 6 do //输出第一行 write(w[i]:4); writeln; t:=0; for i:=1 to d do begin //输出第二行前面的的空格。d如是0,不进入循环 write(' ':4); inc(t); end; dd:=a[m]; //当月有几天 if( (m=2) and leapy(y+1)) then inc(dd); //如是闰年2月,要加一天 for i:=1 to dd do begin write(i:4); inc(t); if(t mod 7=0) then writeln; //t为7的倍数,换行 end; writeln; end.
嗯,好像不太复杂吧。输入年、月,就能打印出日历,很COOL的样子 ^_^
到此结束,有问题请留言。