1008. 二哥买期货

Description

二哥想知道在一段时期内,一共有多少个交易日。期货交易日的限定如下:

  1. 周六、周日不能交易

  2. 元旦期间(1月1日)不能交易

  3. 五一劳动节期间(5月1日至3日)不能交易

  4. 十一国庆节期间(10月1日至7日)不能交易

  5. 没有在上述要求中提到的日期均可交易

Input Format

第一行有一个整数n,表示一共有n组数据。

每组数据都有一行,是两个用空格分开的日期,分别为开始日期和结束日期。日期格式为YYYY-MM-DD(比如2010-11-11);数据保证开始日期不晚于结束日期。

对于所有数据:n365 

对于30%的数据:日期范围从2010-11-23至2012-12-21

对于70%的数据:日期范围从1900-01-01至9999-12-31

Output Format

输出共n行,每行一个整数,对应于一组数据。

每组数据需要输出在指定日期区间内,共有多少个交易日;区间的开始和结束日期也算在内(如果是交易日的话)。

Sample Input

4
2010-11-18 2010-11-20
2010-01-01 2010-01-01
2010-05-01 2010-05-03
2010-10-01 2010-10-07

Sample Output

2
0
0
0

这个题目主要考虑日期方面的一些算法,这里假设左边的日期为date1,右边的日期为date2,由于题目保证date1在date2之前,所以我们可以首先计算出date2-date1,如何求出date2-date1?

这里首先介绍日期方面的第一个算法,大家如果有使用过mktime函数的话可能会了解,mktime函数在linux代码中的实现如下所示:

static inline unsigned long
mktime (unsigned int year, unsigned int mon,
 unsigned int day, unsigned int hour,
 unsigned int min, unsigned int sec)
{
 if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */
  mon += 12;  /* Puts Feb last since it has leap day */
  year -= 1;
 }

 return (((
  (unsigned long) (year/4 - year/100 + year/400 + 367*mon/12 + day + year*365 - 719499
     )*24 + hour /* now have hours */
   )*60 + min /* now have minutes */
 )*60 + sec; /* finally seconds */
http://blog.csdn.net/axx1611/article/details/1792827上对这个进行了详细的解析,我们可以通过

year/4 - year/100 + year/400 + 367*mon/12 + day + year*365  - 719499求解一个date相对于1年1月1日的天数,这里我们并不关注绝对相差值,只需要知道日期之间的相对差,所以719499是不必要的,只需要通过year/4 - year/100 + year/400 + 367*mon/12 + day + year*365求解即可,那么我们可以实现date之间的日子数,下面一个问题是如何排除那些节日和周末?

那么很值得讨论的一个问题是如何确定一天到底是星期几?这里有一个和上述差不多的算法,

int dayofweek(int year,int month,int day)
{
    static int t[]={0,3,2,5,0,3,5,1,4,6,2,4};
    year-=month<3;
    int ans=(year+year/4-year/100+year/400+t[month-1]+day)%7;
    if(ans==0) ans=7;
    return ans;
}

对于date1到date2,假设date1为星期k,date2为星期t,date1到date2之间周一到周日的循环数为cycle,则有(7-k+1)+7*cycle+t=date2-date1,由此可以确定date1到date2之间的星期日和星期六数目,但是这里面可能有一些日子既是周末也是节日,那么我们需要计算重复的日子,对于date1到date2这些年份的所有节日都求出和1年1月1日的相差,放在一个vector里面,那么对于那些在date1到date2之间的节日计算其是否为星期六和星期日。

#include <iostream>
#include <vector>
using namespace std;
struct date
{
    int year;
    int month;
    int day;
};
int dayofweek(int year,int month,int day)
{
    static int t[]={0,3,2,5,0,3,5,1,4,6,2,4};
    year-=month<3;
    int ans=(year+year/4-year/100+year/400+t[month-1]+day)%7;
    if(ans==0) ans=7;
    return ans;
}
int days_between_dates(int year,int month,int day)
{
    if((month-=2)<=0)
    {
        year--;
        month+=12;
    }
    return (year/4 - year/100 + year/400 + 367*month/12 +day + year*365);
}
int festivals(int year1,int year2,vector<int>& result)
{
    for(int i=year1;i<=year2;i++)
    {
        result.push_back(days_between_dates(i,1,1));
        result.push_back(days_between_dates(i,5,1));
        result.push_back(days_between_dates(i,5,2));
        result.push_back(days_between_dates(i,5,3));
        result.push_back(days_between_dates(i,10,1));
        result.push_back(days_between_dates(i,10,2));
        result.push_back(days_between_dates(i,10,3));
        result.push_back(days_between_dates(i,10,4));
        result.push_back(days_between_dates(i,10,5));
        result.push_back(days_between_dates(i,10,6));
        result.push_back(days_between_dates(i,10,7));
    }
    return 0;
}
int main()
{
    int n;
    char a;
    cin>>n;
    date tp1;
    date tp2;
    for(int i=0;i<n;i++)
    {
        int total=0;
        vector<int> res;
        cin>>tp1.year>>a>>tp1.month>>a>>tp1.day;
        cin>>tp2.year>>a>>tp2.month>>a>>tp2.day;
        festivals(tp1.year,tp2.year,res);
        int ans1=dayofweek(tp1.year,tp1.month,tp1.day);
        int ans2=dayofweek(tp2.year,tp2.month,tp2.day);
        int left_year=days_between_dates(tp1.year,tp1.month,tp1.day);
        int right_year=days_between_dates(tp2.year,tp2.month,tp2.day);
        int cycle=(right_year-left_year-7+ans1-ans2)/7;
        total+=(right_year-left_year+1)-cycle*2;
        if(ans1<=6) total=total-2;
        else total=total-1;
        if(ans2>=6) total=total-(ans2-5);
        int i1=-1,j1=res.size();
        while(res[++i1]<left_year);
        while(res[--j1]>right_year);
        total=total-(j1-i1+1);
        for(int t=i1;t<=j1;t++)
        {
            int tp=(res[t]-left_year+ans1)%7;
            if(tp==0||tp==6) total++;
        }
        cout<<total<<endl;
    }
    return 0;
}

由于问题前后改了很多遍,代码可能显得混乱。