欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

从“HDU 2005 第几天?”谈起

程序员文章站 2022-05-15 22:31:32
在程序设计中,日期时间的处理经常会遇到。在C语言程序设计的一些教材中会出现如下例子或习题。 【例1】第几天? (HDU 2005) 给定一个日期,输出这个日期是该年的第几天。 Input输入数据有多组,每组占一行,数据格式为YYYY/MM/DD组成,具体参见sample input ,另外,可以向你 ......

      在程序设计中,日期时间的处理经常会遇到。在c语言程序设计的一些教材中会出现如下例子或习题。

【例1】第几天? (hdu 2005)

      给定一个日期,输出这个日期是该年的第几天。

input
输入数据有多组,每组占一行,数据格式为yyyy/mm/dd组成,具体参见sample input ,另外,可以向你确保所有的输入数据是合法的。

output
对于每组输入数据,输出一行,表示该日期是该年的第几天。

sample input
1985/1/20
2006/3/12

sample output
20
71

      (1)编程思路1。

      对于month月,需要累计1~month-1月的各个月份的天数。例如,month等于8,需要累计1~7月的天数,即d=0+31(1月)+28(或29、2月天数)+31(3月)+30(4月)+31(5月)+30(6月)+31(7月),这个操作可以写成循环,如:

         d=0;

         for( i=1; i<=month-1; i++)

              d=d+第i月的天数;

      但也可以不写成循环,用switch…case结构来解决。因为,大的月份一定包含小的月份的累计,因此,在case常量表达式的安排时,可以从大到小,并且每个入口进入后,不用break语句退出switch结构,这样可以完成累计,具体描述为:

    d=0;

           switch(month-1)

           {

         case 11:d+=30;

         case 10:d+=31;

         case 9:d+=30;

         case 8:d+=31;

         case 7:d+=31;

         case 6:d+=30;

         case 5:d+=31;

         case 4:d+=30;

         case 3:d+=31;

         case 2:d+=28(或d+=29);

         case 1:d+=31;

      }

      另外,在程序中,需要判断某一年是否闰年,因为闰年的2月份为29天,而非闰年的2月份为28天。

      闰年的判定条件是:①能被4整除,但不能被100整除的年份都是闰年,如1996年,2004年是闰年;②能被100整除,又能被400整除的年份也是闰年。如2000年是闰年。可以用一个逻辑表达式来表示:

            (year%4==0&&year%100! =0) | | year%400==0

      当year为某一整数值时,如果上述表达式值为true(1),则year为闰年;否则year为非闰年。

      (2)源程序1。

        #include <stdio.h>

int main()
{
      int year,month,day,d;
      while (scanf("%d/%d/%d",&year,&month,&day)!=eof)
     {
           d=0;
           switch(month-1)
           {
                 case 11:d+=30;
                 case 10:d+=31;
                 case 9:d+=30;
                 case 8:d+=31;
                 case 7:d+=31;
                 case 6:d+=30;
                 case 5:d+=31;
                 case 4:d+=30;
                 case 3:d+=31;
                 case 2:d+=28;
                      if (year%4==0 && year%100!=0 || year%400==0) d++;
                 case 1:d+=31;
           }
           d=d+day;
           printf("%d\n",d);
      }
      return 0;
}

      (3)编程思路2。

      学习过数组后,我们可以将每个月的天数保存在数组table[13]中,其中table[i]的值为第i月的天数。这样用一个循环完成累加即可。2月先保存默认天数28,对闰年进行特别处理。

      这样写出的程序更简洁。在我们学习的过程中,应将写出简洁高效的程序作为自己程序设计的风格和目标。

      (4)源程序2。

#include <stdio.h>
int main()
{
      int table[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
      int year,month,day,d,i;
      while (scanf("%d/%d/%d",&year,&month,&day)!=eof)
      {
            d=0;
            for (i=1;i<=month-1;i++)
                  d+=table[i];
            if (month>2 && (year%4==0 && year%100!=0 || year%400==0))
                  d++;
            d=d+day;
            printf("%d\n",d);
      }
      return 0;
}

【例2】几月几日。      

      输入一个年份year和一个整数d,求year年的第d天是几月几日? 例如,输入2006  71,输出应为3  12,即2006年的第71天是3月12日。

      (1)编程思路。

      执行例1中程序的逆过程。为简化计,程序中采用二维数组来分别保存非闰年和闰年各月的天数。用循环先确定第d天前有多少月,减去各月的天数后,剩余的就是几日。

      (2)源程序。

#include <stdio.h>
int main()
{
       int table[2][13]={{0,31,28,31,30,31,30,31,31,30,31,30,31}
                                ,{0,31,29,31,30,31,30,31,31,30,31,30,31}};
      int year,month,day,d,leap;
      while (scanf("%d %d",&year,&d)!=eof)
      {
           month=1;
           if (year%4==0 && year%100!=0 || year%400==0)
                leap=1;
          else
               leap=0;
          while (d>table[leap][month])
          {
                d-=table[leap][month];
                month++;
          }
          day=d;
          printf("%d %d\n",month,day);
      }
      return 0;
}

【例3】今天星期几。

       输入一个日期的年、月、日后,输出该日期是星期几。

      (1)编程思路。

      设变量year、month和day分别表示输入的年、月和日。在例1中我们求了一个日期是当年的第几天(设为d),由于一个星期有7天,因此,如果我们知道该年的元旦是星期几(设为week),那么该日期是星期几就可以确定了,计算公式为:(week+d-1)%7。0代表星期日。

      怎样求年号为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;

      (2)源程序。

#include <stdio.h>
int main()
{
      int table[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
      char str[7][3]={"日","一","二","三","四","五","六"};
      int year,month,day,d,i,week;
      while (scanf("%d/%d/%d",&year,&month,&day)!=eof)
      {
            week=(year+(year-1)/4-(year-1)/100+(year-1)/400)%7;
            d=0;
            for (i=1;i<=month-1;i++)
                d+=table[i];
            if (month>2 && (year%4==0 && year%100!=0 || year%400==0))
                d++;
            d=d+day;
            week=(week+d-1)%7;
            printf("%d 年 %d 月 %d日 星期%s\n",year,month,day,str[week]);
      }
      return 0;
}

 【例4】10月21日。(hdu 1491)

      2006年10月21日是杭州电子科技大学50周年校庆日。输入一个2006年的日期,输出其距离校庆日还有多少天。

      (1)编程思路。

      2006年是非闰年,因此不需要判断闰年。10月21日是当年第294天。根据输入的日期计算该日期是2006年的第几天(设为d),然后根据d与294的大小关系,输出相应的结果。

      (2)源程序。

#include <stdio.h>
int main()
{
      int table[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
      int month,day,d,i,t;
      scanf("%d",&t);
      while (t--)
      {
             scanf("%d%d",&month,&day);
             d=0;
             for (i=1;i<=month-1;i++)
                   d+=table[i];
             d=d+day;
             if (d<294)
                    printf("%d\n",294-d);
            else if (d==294)
                    printf("it's today!!\n");
            else
                    printf("what a pity, it has passed!\n");
       }
      return 0;
}