-1

I'm currently working on a problem (Friday the thirteenth) on the USACO site and I've coded a program which, given a number N,computes how often the 13th lands on each day of the week from Jan 1st,1900 to Dec 31st,1900+N-1.The algorithm is inspired from a mental calculation trick : http://www.jimloy.com/math/day-week.htm

Things to keep in mind: January 1, 1900 was on a Monday. Thirty days has September, April, June, and November, all the rest have 31 except for February which has 28 except in leap years when it has 29. Every year evenly divisible by 4 is a leap year (1992 = 4*498 so 1992 will be a leap year, but the year 1990 is not a leap year) The rule above does not hold for century years. Century years divisible by 400 are leap years, all other are not. Thus, the century years 1700, 1800, 1900 and 2100 are not leap years, but 2000 is a leap year.*

The program works well except when N=256 (1900 to 2156) My program outputs : 440 438 439 439 437 439 440

while on USACO, there is: 440 439 438 438 439 439 439

The program takes a file (friday.in) containing N as input and outputs seven space separated integers representing the number of occurrences of each day (starting by Saturday) on one line in another file(friday.out).

I've put several cout for debugging purposes.

Here's the code:

/*
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;
}
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356

1 Answers1

1

You get wrong results from 205 years on, that is from the year 2104.

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

For years divisible by 4, you don't subtract the non-leap years 2100, 2200, 2300, 2500, ... from the count.

Fix:

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

subtract them. That leaves the wrong leap year count for years not divisible by 4 after 2300, you can correct that by adding a similar correction (and that doesn't need multiple branches then):

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

The formula shall determine the number of leap years that have passed between 1900 and m_year. If m_year is not a multiple of 4, the simple "multiples of 4 are leap years" gives that count as (m_year - 1900)/4. But that doesn't take into account that multiples of 100 are not leap years in general, so we subtract the number of such years that have passed in between, - (m_year - 1900)/100. However, now the multiples of 400 that are leap years have been subtracted too, so add their count back, + (m_year - 1600)/400 (base year here is 1600, the largest multiple of 400 not after 1900).

For multiples of 4, we have to correct for the fact that the current year is not a leap year before the current year, so I let the correction take place only after and shift the base years.

Better fix, making the leap year count uniform (no branches):

int leapDays = (m_year - 1901)/4 + (m_year-1601)/400 - (m_year-1901)/100;
Daniel Fischer
  • 181,706
  • 17
  • 308
  • 431
  • could you explain better the two fixes (I've understood where the problem is but i don't understand your solution well) – user1560788 Jul 30 '12 at 23:30
  • You calculate the leap years from 1900 to the year in question. Basically, that's `(year-1900)/4`. But that doesn't take into account the `xx00` years. So we subtract the number of those between 1900 and the current year `- (m_year-1900)/100`, but now we subtracted also the multiples of 400, so we add them back in `+ (m_year-1600)/400`. For multiples of 4, the current year is not a leap year _between_ 1900 and current, so I shifted the correction by 4 years for the non-leap-year status of 2100 to influence only years after. – Daniel Fischer Jul 30 '12 at 23:43