zoukankan      html  css  js  c++  java
  • 从“HDU 2005 第几天?”谈起

          在程序设计中,日期时间的处理经常会遇到。在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 ",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 ",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 ",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 ",year,month,day,str[week]);
          }
          return 0;
    }

    弄懂了本例的编程思路,可以自己做一下 HDU 2133 “What day is it”。将上面的源程序略作修改即可。

     【例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 ",294-d);
                else if (d==294)
                        printf("It's today!! ");
                else
                        printf("What a pity, it has passed! ");
           }
          return 0;
    }

     【例5】今夕何夕。 (HDU 6112)

          2019 年10月1日是中华人民共和国建国70周年。这天是星期二。思考这样一个问题:接下来最近的哪一年里的同一个日子,和建国70周年的星期数一样?比如2019年10月1日,星期二。下一个也是星期二的10月1日发生在2024年。

          输入一个日期yyyy-mm-dd,输出接下来与该日期的星期数相同的同一个日子的年份。
          (1)编程思路。

          根据例3的公式可以计算出year年元旦是星期几(设为week1),然后用i从year+1年开始穷举,看哪一年的同一个日子的年份的星期符合要求。

          由于两个日期年份不同,月和日相同,即它们在该年属于第几天相差最多为1(因为闰年的缘故)。因此可以根据公式计算出穷举的第i年元旦是学期几(设为week2)。

          若给定日期在1月或2月(2月29日作为特例特别处理,其方法是穷举下一个闰年),则只要week1==week2,第i年即为所求。

          若给定日期的月份大于3月,需考虑闰年的问题。若第year和第i年均是闰年或均不是闰年,无需校正,直接比较week1和week2即可。

          若第year年是闰年而第i年不是闰年,由于相同的日子,第year年会比第i年多一天,因此应该week1+1==week2,两个日期的星期数才相同。将week2用表达式 week2=(week2-1+7)%7 校正一下。

          若第year年不是闰年而第i年是闰年,由于相同的日子,第year年会比第i年少一天,因此应该week1-1==week2,两个日期的星期数才相同。将week2用表达式 week2=(week2+1)%7 校正一下。

          这样再比较 week1和week2是否相等才行。

          (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))        // 2月29日特别处理
                               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 ",i);
                               break;
                       }
              }
          }
          return 0;

  • 相关阅读:
    通过HttpListener实现简单的Http服务
    WCF心跳判断服务端及客户端是否掉线并实现重连接
    NHibernate初学六之关联多对多关系
    NHibernate初学五之关联一对多关系
    EXTJS 4.2 资料 跨域的问题
    EXTJS 4.2 资料 控件之Grid 那些事
    EXTJS 3.0 资料 控件之 GridPanel属性与方法大全
    EXTJS 3.0 资料 控件之 Toolbar 两行的用法
    EXTJS 3.0 资料 控件之 combo 用法
    EXTJS 4.2 资料 控件之 Store 用法
  • 原文地址:https://www.cnblogs.com/cs-whut/p/11511301.html
Copyright © 2011-2022 走看看