SzNOI之d100题解题报名,日历问题

SzNOI之d100题解题报名,日历问题

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.
View Code

三、打印日历

有了前面的铺垫,解题报告正式开始。

似乎、好像、仿佛,已经没有什么好说的了,直接看程序:

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的样子 ^_^

到此结束,有问题请留言。

TOP

原文地址:https://www.cnblogs.com/xin-le/p/4067876.html