首页 > ACM题库 > HDU-杭电 > Hdu 1635 Double Time-时间计算[解题报告] C++
2013
12-16

Hdu 1635 Double Time-时间计算[解题报告] C++

Double Time

问题描述 :

In 45 BC a standard calendar was adopted by Julius Caesar–each year would have 365 days, and every fourth year have an extra day–the 29th of February. However this calendar was not quite accurate enough to track the true solar year, and it became noticeable that the onset of the seasons was shifting steadily through the year. In 1582 Pope Gregory XIII ruled that a new style calendar should take effect. From then on, century years would only be leap years if they were divisible by 400. Furthermore the current year needed an adjustment to realign the calendar with the seasons. This new calendar, and the correction required, were adopted immediately by Roman Catholic countries, where the day following Thursday 4 October 1582 was Friday 15 October 1582. The British and Americans (among others) did not follow suit until 1752, when Wednesday 2 September was followed by Thursday 14 September. (Russia did not change until 1918, and Greece waited until 1923.) Thus there was a long period of time when history was recorded in two different styles.Write a program that will read in a date, determine which style it is in, and then convert it to the other style.

输入:

Input will consist of a series of lines, each line containing a day and date (such as Friday 25 December 1992). Dates will be in the range 1 January 1600 to 31 December 2099, although converted dates may lie outside this range. Note that all names of days and months will be in the style shown, that is the first letter will be capitalised with the rest lower case. The file will be terminated by a line containing a single `#’.

输出:

Output will consist of a series of lines, one for each line of the input. Each line will consist of a date in the other style. Use the format and spacing shown in the example and described above. Note that there must be exactly one space between each pair of fields. To distinguish between the styles, dates in the old style must have an asterisk (`*’) immediately after the day of the month (with no intervening space). Note that this will not apply to the input.

样例输入:

Saturday 29 August 1992
Saturday 16 August 1992
Wednesday 19 December 1991
Monday 1 January 1900
#

样例输出:

Saturday 16* August 1992
Saturday 29 August 1992
Wednesday 1 January 1992
Monday 20* December 1899


这是我在UVa OJ中第一次碰到有关时间和日期的计算题目。由所给信息可知,旧历的1582年10月5日与新历的1582年10月15日应该是同一天。从旧历的元年1月1日至1582年10月5日的天数为:1581×365 + 1581÷4 + 273 + 15 – 1= 577737。式中273为当年1月到10月的累积天数,后面-1是由于我们将元年的1月1日定为第0天,因此每个月应从0号开始计算。从新历的元年1月1日至1582年10月15日的天数为:1581×365 + 1581÷4 – 1581÷100 + 1581÷400 + 273 + 15 – 1= 577735。我们发现,新历和旧历的元年1月1日并不是同一天,新历的要晚两天。

接下来要做的是判断输入的日期是哪一种历法。看来没有其它办法,只能通过星期几是否和日期相符来判断了。但会不会存在新旧历中两个相同的日期,它们的星期几也相同呢?回答是肯定的,1300年的3月28日在新旧历上都是星期日,2100年的3月1日都是星期一。然而您尽可以放心,在题目所给的范围内:1600年1月1日至2099年12月31日是不存在这样的情况的。

这样思路就理清了,先将日期的星期几计算出来,判定是哪种历法。再转为自元年1月1日至那一天的累计天数,经过较正后(加减2天),用另一种历法反算回来就可以了。首先是计算星期几的算法,存在这样一个事实,不管日期是否连续,星期几永远是连续的(由0到7再回到0)。按新历从现在已知的某一天向前推算,可知新历的元年1月1日(第0天)是周一,那么任何日期转化所得的累计天数加1再除7的余数即是其在新历下的星期几。

接下来就是将累计天数转为指定历法下的日期。如果是要转换到新历法,由于每100年的天数是不一样,因此要先按400年的总天数取整计算,再按100年的总天数取整计算。这个过程用语言描述很困难,还是看代码吧,注释很详尽了。此外,做这道题一定要细心,有很多情况需要考虑。在提交前最好能生成一个几百年的日期表测试一下。

一个从1600-2100年的老黄历,也许能提供帮助。

#include <algorithm>
#include <functional>
#include <iostream>
#include <string>
using namespace std;
//平年和闰年的各月累计天数表
static int aMDays[] =  {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
static int aMDaysL[] = {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335};
//根据指定的历法判定闰年。bNew为true代表新历法,fase代表旧历法
bool LeapYear(int Year, bool bNew) {
    if (bNew) {
        //标准的闰年判断
        return ((Year % 4 == 0 && Year % 100 != 0) || Year % 400 == 0);
    }
    return (Year % 4 == 0);
}
//将年月日换算为从元年1月1日(第0天)到这一天的天数
int Date2Days(int Year, int Month, int Date, bool bNew) {
    --Year; //年份定为以0年起始
    //年份乘以365天,加上闰年数(总置闰天数)
    //如果为新历法,要减去百年中不能被400整除的置闰天数
    int Days = Year * 365 + Year / 4 + (bNew ? (Year / 400 - Year / 100) : 0);
    //如果为闰年且月分大于2,要闰1天
    Days += ((Month > 2) ? LeapYear(Year + 1, bNew) : 0);
    //返回:年累计天数+月累计天数+日期-新旧历法起点对齐
    return (Days + aMDays[Month - 1] + Date - 1 - (!bNew * 2));
}
//主函数
int main(void) {
    //月分和星期的字符串表,用于处理输入和输出
    const static string aDays[] = {"Sunday", "Monday", "Tuesday",
        "Wednesday", "Thursday", "Friday", "Saturday"};
    const static string aMonths[] = {"January", "February", "March",
        "April", "May", "June", "July", "August", "September",
        "October", "November", "December"};
    //400年的天数,100年的天数,4年的天数和1年的天数常量
    const int nDays400Y = 400 * 365 + 100 - 3;
    const int nDays100Y = 100 * 365 + 25 - 1;
    const int nDays4Y = 4 * 365 + 1, nDays1Y = 365;
    //循环处理每一个输出的日期
    for (string str; cin >> str && str != "#";) {
        int Day, Date, Month, Year;
        //在字符串表中检索是周几
        Day = find(&aDays[0], &aDays[7], str) - &aDays[0];
        //读入年月日
        cin >> Date >> str >> Year;
        //在字符串表中检索是几月
        Month = find(&aMonths[0], &aMonths[12], str) - &aMonths[0] + 1;
        //将当前日期按旧历换算为天数
        int nDays = Date2Days(Year, Month, Date, false);
        bool bOld2New = true; //为真表示由旧历转新历
        //判定算得的星期是否与输入的相符,若相符则为旧历纪年
        if (Day == (nDays + 1) % 7) {
            //转新历时要先处理400年和100年的置闰
            Year = nDays / nDays400Y * 400; //算出第几个400年
            nDays %= nDays400Y;
            //算出400年中的第几个百年
            if (nDays == nDays100Y * 4) { //该400年的最后一天
                Year += 300;
                nDays -= nDays100Y * 3;
            }
            else {
                Year += nDays / nDays100Y * 100;
                nDays %= nDays100Y; //取零头
            }
        } //否则为亲历纪年,重新计算天数,并加2与旧历对齐
        else {
            nDays = Date2Days(Year, Month, Date, true) + 2;
            Year = bOld2New = 0;
        }
        //算出百年中的第几个4年
        Year += nDays / nDays4Y * 4;
        nDays %= nDays4Y; //取零头
        //算出4年中的第几年
        if (nDays == nDays1Y * 4) { //年4年的最后一天
            Year += 3;
            nDays -= nDays1Y * 3;
        }
        else {
            Year += nDays / nDays1Y;
            nDays %= nDays1Y; //取零头
        }
        //判定闰年,选取相应的各月累计天数表
        int *pMDays = (LeapYear(++Year, bOld2New) ? aMDaysL : aMDays);
        //按当年累计天数查找月份
        for (Month = 0; Month < 12 && pMDays[Month] <= nDays; ++Month);
        //计算当月日期
        Date = nDays - pMDays[Month - 1] + 1;
        //计算星期几
        Day = (Date2Days(Year, Month, Date, bOld2New) + 1) % 7;
        //按格式要求输出结果
        cout << aDays[Day] << ' ' << Date << (bOld2New ? " " : "* ");
        cout << aMonths[Month - 1] << ' ' << Year << endl;
    }
    return 0;
}

转自:http://www.cnblogs.com/devymex/archive/2010/08/21/1805051.html