我目前在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;
}
答案 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;