usaco:星期五十三错误

时间:2012-07-30 21:14:30

标签: c++ algorithm

我目前在USACO网站上处理一个问题(星期五,十三号),我编写了一个程序,给出一个数字N,计算从1月1日开始的每周第13次登陆的频率, 1900年12月31日,1900 + N-1。该算法的灵感来自心理计算技巧:http://www.jimloy.com/math/day-week.htm

要记住的事情: 1900年1月1日星期一。 三十天有九月,四月,六月和十一月,所有其余的都有31个,除了二月有28个,除了闰年有29个。 每年可被4整除的是闰年(1992 = 4 * 498,因此1992年将是闰年,但1990年不是闰年) 上述规则不适用于世纪年。可以被400整除的世纪年是闰年,其他都不是。因此,世纪1700年,1800年,1900年和2100年不是闰年,而2000年是闰年。*

该程序运行良好,除非N = 256(1900至2156) 我的课程输出: 440 438 439 439 437 439 440

在USACO上,有: 440 439 438 438 439 439 439

程序获取一个包含N作为输入的文件(friday.in),并在另一个文件(friday.out)的一行上输出七个空格分隔的整数,表示每天出现的次数(从星期六开始)。

我已经把几个cout用于调试目的。

以下是代码:

/*
ID: freebie1
PROG:friday
LANG: C++
*/

#include <fstream>
#include <vector>
#include <iostream>
using namespace std;

class Date
{
    public:
    Date(int month=0,int year=0):m_month(month),m_year(year)
    {

    }
    int monthDiff()
    {
        if(m_month<=3)
        {
            if(m_month==1)
            {
                    return 0;
            }
            else if(m_month==2)
            {
                    return 31;
            }
        }
        if(m_month>=3)
        {
            if((m_year%4==0 && m_year%100!=0)||(m_year%100==0 && m_year%400==0))
            {
                if(m_month<9)
                {
                    if(m_month%2==0)
                        return 60+(31*int(m_month/2.6)+30*(int(m_month/2.6)-1));
                    else
                        return 60+(61*int(m_month/3.5));

                }

                else if(m_month>=9)
                {

                    if(m_month%2==0)
                        return 91+61*(m_month/3);
                    else
                        return 91+(31*int(m_month/2.75)+30*int(m_month/3.6));
                }
            }
            else
            {
                if(m_month<9)
                {
                    if(m_month%2==0)
                        return 59+(31*int(m_month/2.6)+30*(int(m_month/2.6)-1));
                    else
                        return 59+(61*int(m_month/3.5));

                }

                else if(m_month>=9)
                {

                    if(m_month%2==0)
                        return 90+61*(m_month/3);
                    else
                        return 90+(31*int(m_month/2.75)+30*int(m_month/3.6));
                }
            }

        }
    }
    void show()
    {
        cout<<m_month<<"/"<<m_year<<": ";
    }
    int tellDay()
    {
        int daysInYears=int((m_year-1900))*365;
        int daysInMonths=this->monthDiff();
        int leapDays;
        if(m_year%4==0 && m_year!=1900)
            leapDays=int((m_year-1900)/4)-1;
        else if(m_year>2100)
            leapDays=int((m_year-1900)/4)-1;
        else if(m_year>2200)
            leapDays=int((m_year-1900))/4-2;
        else
            leapDays=int((m_year-1900))/4;

        int days=13+leapDays;
        int res=daysInYears+daysInMonths+days;
        cout<<"MonthDiff: "<<this->monthDiff()<<" In years: "<<daysInYears<<" days: "<<days<<"   ";
        return res%7;
    }
    private:
    int m_month;
    int m_year;
};

int main()
{
    ifstream fin("friday.in");
    ofstream fout("friday.out");

    if(fin)
    {
        int n(0),day(0),sMonth(1),sYear(1900);
        fin>>n;
        vector<int> weekDays(7,0);
        for(int i(0);i<n;i++)
        {
            for(int j(0);j<12;j++)
            {
                Date date(sMonth+j,sYear+i);
                day=date.tellDay();
                date.show();
                cout<<day<<endl;
                switch(day)
                {
                    case 0:
                    weekDays[1]+=1;
                    break;
                    case 1:
                    weekDays[2]+=1;
                    break;
                    case 2:
                    weekDays[3]+=1;
                    break;
                    case 3:
                    weekDays[4]+=1;
                    break;
                    case 4:
                    weekDays[5]+=1;
                    break;
                    case 5:
                    weekDays[6]+=1;
                    break;
                    case 6:
                    weekDays[0]+=1;
                    break;
                }

            }

        }
        for(int i(0);i<6;i++)
        {
            fout<<weekDays[i]<<" ";
        }
        fout<<weekDays[6]<<endl;
    }

    return 0;
}

1 个答案:

答案 0 :(得分:1)

从205年开始,你会得到错误的结果,即2104年。

if(m_year%4==0 && m_year!=1900)
    leapDays=int((m_year-1900)/4)-1;

对于可被4整除的年份,您不会从计数中减去2100年,2200年,2300年,2500年......的非闰年。

修正:

if(m_year%4==0 && m_year!=1900)
    leapDays=int((m_year-1900)/4)-1 + (m_year-1604)/400 - (m_year-1904)/100;

减去它们。这使得错误的闰年计数在2300之后不能被4整除,你可以通过添加类似的修正来校正(那时不需要多个分支):

else
    leapDays=int((m_year-1900)/4) + (m_year-1600)/400 - (m_year-1900)/100;

公式应确定在 1900和m_year之间已经过的闰年数。如果m_year不是4的倍数,则简单的“4的倍数是闰年”会将该计数计为(m_year - 1900)/4。但是,这并没有考虑到100的倍数一般不是闰年,所以我们减去已经过去的年数- (m_year - 1900)/100。但是,现在已经减去了 闰年的400的倍数,所以加上他们的计数,+ (m_year - 1600)/400(这里的基数为1600,不是1900年后的400的最大倍数)

对于4的倍数,我们必须纠正当前年度当前年份之前的闰年这一事实,所以我只能在基准年之后进行修正

更好的修复,使闰年统一(没有分支):

int leapDays = (m_year - 1901)/4 + (m_year-1601)/400 - (m_year-1901)/100;