编程马拉松第一题续

  我又食言了。

  上篇说好了明天继续,结果拖到了今天。不过这几天我也没有闲着,天天都有在debug。

  不得不说debug真的是考验一个码农的耐心、细心和想象力的事情。

  我这三天在闲下来的时候就会想想,到底是什么情况没有考虑到:是还有没想到的边界条件?是HDU服务器上的编译器和VS2010的差别?最后的找到的原因也真的是让我很无语。这里不禁要抱怨一下HDU ACM的Online Judge系统,您就不能把测试用例放出来么?至少把没通过的测试用例放出来行不行啊?到底什么地方错了还要自己去猜,真的是很让人抓狂的啊!

  其实上篇给出的程序还是错误的,在一些边界情况下会得到错误的结果。上篇着重讲基础知识,所以这不影响。而正好这可以当成反面教材,用来这次讲debug的过程。

  首先,假设你把这个程序放到了Online Judge里面跑,满心欢喜地认为要结束了,结果系统返回了个Wrong Answer! 肿么办!?

  1.考虑用更多的测试用例,来检查结果是否正确。但是测试用例并不好取,随机取的话,要手工计算结果也很费事。所以可以考虑用365,3650,36500这些值方便计算。

  上面的例子测试都通过了怎么办?

  2.这时就要多考虑边界的情况了。因为36500这种值都通过了说明闰年的计算都是没问题的。那么边界的情况有哪些呢?边界情况可以是一年的结束或者结尾,比如2000.12.31,2600.1.1;还可以是平年或者闰年的2月最后一天,比如2004.2.29,2100.2.28,2100.3.1;还可以是多年后(或多年前)的当天(或前天、明天),比如1999.3.24,3000.3.22。另外,有个小技巧值得注意:在判断语句中的>和<号要考虑==情况是不是也能正常运行,对边界问题往往也很有帮助。

  如果有例子测试出来结果是错的,但是找不出错在哪怎么办?

  3.这时就要借助Visual Studio里面的debug工具了。运用工具,比自己在程序里添加输出语句调试要方便的多,也快速的多。Debug的方法具体参见博客:http://gjianw217.blog.163.com/blog/static/2614418201222994650926/

有了工具,还要说debug的方法。对于出错的例子,可以手算出程序运行每一步,对应变量应该出来的结果是什么,然后在debug的时候进行对照,找出出错的地方。

  如果还是有问题怎么办?

  4.这个时候可能你看着程序头都大了,不如试着去做代码的简化和模块化,并且做一些注释。这可以调节一下纠结的逻辑混乱的大脑,并且多做一些模块化的工作,比如抽象出来更多的类或者函数,画一张类与函数关系图,这对后续的debug作用和启发也很大。实在不行,把这个代码放几天,每天就花几分钟去想想,不要浪费太多的时间。有时脱身出来,才能想到更广阔的问题。

  经过漫长的三天,我的程序终于调试好了!

  下面上代码:

View Code
  1 #include <iostream>
  2 #include <iomanip>
  3 using namespace std;
  4 int year=2013,
  5     month=3,
  6     day=24;
  7 int monthDay[2][13]={    {0,31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
  8                         {0,31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
  9 bool IsLeapYear(int x)
 10 {
 11     return x%400==0 || x%100!=0&&x%4==0 ;
 12 }
 13 void Xiaoq(int D)
 14 {
 15     int delta=D,
 16         y=year,
 17         m=month,
 18         d=day;
 19 
 20     y=year+delta/365;            //计算穿越到的大致年份
 21     delta=delta-delta/365*365;    //计算穿越天到该年份同一天相差的天数delta
 22     if( delta==0 )                //保证delta是个正数。如果delta==0,那么就回溯365天。
 23     {
 24         y--;
 25         delta=365;
 26     }
 27     for(int yit=year; yit<y+1; yit++)    //根据穿越过的闰年,进行delta微调,调整时也要保证delta是个正数
 28     {
 29         if( IsLeapYear(yit) )
 30         {
 31             --delta;
 32         }
 33     }
 34     if( delta<0 )                    //如果delta<0,那么需要回溯一年
 35     {
 36         delta= (IsLeapYear(y)?366:365) +delta;
 37         y--;
 38     }
 39 
 40     while(delta>0)
 41     {
 42         if( delta>monthDay[IsLeapYear(y)][m] )            //delta肯定小于y年的天数,这时先遍历月份,来减小delta
 43         {
 44             delta=delta-monthDay[IsLeapYear(y)][m];
 45 
 46             m= m+1==13?1:m+1;                                //月份+1时要考虑12月份的边界,以及边界对年的影响
 47             if( m==1 )
 48                 y++;
 49         }
 50         else
 51         {
 52             if( delta>monthDay[IsLeapYear(y)][m]-d)        //月份遍历结束后,delta肯定小于当月的天数,这时遍历天数,来减小delta
 53             {
 54                 d=delta-(monthDay[IsLeapYear(y)][m]-d);
 55                 m= m+1==13?1:m+1;
 56                 if ( m==1)
 57                     y++;
 58             }
 59             else
 60             {
 61                 d=d+delta;
 62             }
 63             delta=0;
 64         }
 65     }
 66     cout<<setfill('0')<<setw(4)<<y<<"/"<<setw(2)<<m<<"/"<<setw(2)<<d<<" ";
 67 }
 68 void Hr(int D)
 69 {
 70     int delta=D,
 71         y=year,
 72         m=month,
 73         d=day,
 74         days=0;
 75 
 76     y=year-delta/365;
 77     delta=delta-delta/365*365;
 78     if( delta==0 )
 79     {
 80         y++;
 81         delta=365;
 82     }
 83     for(int yit=year; yit>y; yit--)
 84     {
 85         if( IsLeapYear(yit) )
 86             delta--;
 87     }
 88     if( delta<0 )
 89     {
 90         delta= (IsLeapYear(y+1)?366:365) +delta;
 91         ++y;
 92     }
 93 
 94     while(delta>0)
 95     {
 96         days= m>1?monthDay[IsLeapYear(y)][m-1]:monthDay[IsLeapYear(y-1)][12];
 97         if( delta>days )
 98         {
 99             delta=delta-days;
100             m= m-1==0?12:m-1;
101             if( m==12)
102                 y--;
103         }
104         else
105         {
106             if( delta>=d )
107             {
108                 d=days-(delta-d);
109                 m= m-1==0?12:m-1;
110                 if( m==12)
111                     y--;
112             }
113             else
114             {
115                 d=d-delta;
116             }
117             delta=0;
118         }
119     }
120     cout<<setfill('0')<<setw(4)<<y<<"/"<<setw(2)<<m<<"/"<<setw(2)<<d<<endl;
121 }
122 int main()
123 {
124     int D;
125     int N=0;
126 
127     cin>>N;
128     for(int i=0;i<N;i++)
129     {
130         cin>>D;
131         Xiaoq(D);
132         Hr(D);
133     }
134     return 0;
135 }

 

  可以看到,代码比上篇有了翻天覆地的变化。简化的地方不谈了。讲讲错到底都出在哪。

  1.在计算完大致年份之后,我认为delta是正的,从而进入后面while循环。然而有两个操作会导致delta<=0,它们是:

delta=delta-delta/365*365;

和后面计算闰年时delta的自减过程。

   2.在月份自减和自加的过程中,没有考虑跨越边界时年份的增减。(至少没有全部考虑到……)

   3.然后这都没有使代码得到Accepted的结果。突然我意识到用于接收输入的数组只开100个是不是太小了!因为每次程序运行时间只有0ms。(虽然最后通过时时间也只有0ms,说明程序时间复杂度本身就低。)

  终于搞定了!生活真美好。

posted @ 2013-03-26 23:23  Henry要当小码农  阅读(244)  评论(0编辑  收藏  举报