sjtu oj 1008 二哥买期货问题

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);数据保证开始日期不晚于结束日期。

对于所有数据:n≤365

对于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;
}

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

时间: 2024-08-02 15:14:25

sjtu oj 1008 二哥买期货问题的相关文章

【算法学习笔记】53.单调队列的简单应用 SJTU OJ 1034 二哥的金链

1034. 二哥的金链 Description 一个阳光明媚的周末,二哥出去游山玩水,然而粗心的二哥在路上把钱包弄丢了.傍晚时分二哥来到了一家小旅店,他翻便全身的口袋也没翻着多少钱,而他身上唯一值钱的就是一条漂亮的金链.这条金链散发着奇异的光泽,据说戴上它能保佑考试门门不挂,RP++.好心的老板很同情二哥的遭遇,同意二哥用这条金链来结帐.虽然二哥很舍不得这条金链,但是他必须用它来付一晚上的房钱了. 金链是环状的,一共有 N 节,老板的要价是 K 节.随便取下其中 K 节自然没问题,然而金链上每一

sjtu oj 1034 二哥的金链

一道比较有意思的题目,我把队列改造了一下然后ac了,后来学数据结构的时候发现我改的队列还有个名词,叫单调队列,心里颇为激动,于是把题目和我的代码发上来与大家分享一下. Description 一个阳光明媚的周末,二哥出去游山玩水,然而粗心的二哥在路上把钱包弄丢了.傍晚时分二哥来到了一家小旅店,他翻便全身的口袋也没翻着多少钱,而他身上唯一值钱的就是一条漂亮的金链.这条金链散发着奇异的光泽,据说戴上它能保佑考试门门不挂,RP++.好心的老板很同情二哥的遭遇,同意二哥用这条金链来结帐.虽然二哥很舍不得

【算法学习笔记】87. 枚举路径 SJTU OJ 1999 二哥找宝藏

这个题只用BFS来搜索一次会很麻烦, 因为每次经过一个宝藏之后,要把所有的vis重置(因为可以重复经过同一点, 但是这样会有很多不必要的路径) 看题目的暗示 最多只有5个宝藏  我们要把所有的宝藏收集齐全, 如果确定了收集的顺序, 那么也就确定了路径 那么可以知道 A55的排列一共是120种路径 遍历起来毫无压力 我们枚举所有宝藏的全排列, 然后从起点开始走, 记录整个路径的步数, 最后取最小值即可. 这里生产全排列的方法利用了 STL的next_permutation函数 非常爽....(要引

【算法学习笔记】54.约瑟夫问题 模拟、逆推动规 SJTU OJ 1038 二哥的约瑟夫

Description 话说二哥当年学习数据结构的时候遇到了那道猴子报数的题目,其实这就是经典的约瑟夫问题. 可是当年的二哥还是个毛头小子,只会用模拟的方法,而其他同学却使用了一些令二哥完全摸不到头脑的方法. ……二哥一怒之下改了题目…… 话说当年花果山的猴子要选大王,选举办法如下: 所有猴子按1-M编号围坐一圈,二哥站在圈中心,由二哥指定一个整数Kn, 之后猴子们从1号开始按顺序报数,报到Kn的猴子退出到圈外,二哥再报出一个整数Kn+1, 然后由刚刚退出的猴子的下一只猴子再开始报数,如此循环报

【算法学习笔记】91.简单森林计数 SJTU OJ 1045 二哥的家族

其实巨水...然而 不用scanf prinf 根本过不了.....真无聊 第一版代码有点问题 效率不高 主要是考虑到这个家族有可能一开始就是个森林 不是从树里分出去的 实际上数据点还是一棵树 然后变成的森林 这样的话只要三个数组就可以了 alive记录是否活着 sons记录每个人的子节点个数 father记录每个人的父节点 可以根据alive[father[x]]判断x是否是一个树的根节点 cnt 维护家族数目 每次死人的时候 判断死的人是某个树的根节点还是只是一个叶子节点 就可以了 #inc

【算法学习笔记】78. STL二分的练习 下标映射的处理技巧 SJTU OJ 1053 二哥的内存

水题也要优化 1.用两个数组单独记录下标的更新 2.用STL中lower_bound来进行二分查找. 要注意lower_bound的返回值意义 是大于等于val的第一个,所以返回值要进行判断才可以利用 #include <iostream> #include <cstdio> #include <algorithm> #include <cstdlib> using namespace std; struct Point { int x; int y; in

【算法学习笔记】41.并查集 SJTU OJ 1283 Mixture

---恢复内容开始--- Description CC非常喜欢化学,并且特别喜欢把一大堆液体倒在一起. 现在CC有n种液体,其中m对会发生反应,现在她想把这n种液体按某种顺序倒入一个容器内,让她获得最刺激的体验,使危险系数尽量大. 我们可以这样计算危险系数,一开始容器内没有任何液体,危险系数为1.每次液体倒入容器时,若容器内已有一种或多种液体会与这种液体发生反应,则危险系数会乘2,否则危险系数不变. 请你求出把这n种液体倒在一起的最大危险系数. Input Format 第一行为两个数n和m.

SJTU OJ 1282 修路 题解

1282. 修路 Description 蹦蹦跳跳结束后,cxt回头看看自己走过的路坑坑洼洼的,心中非常不爽,他表示要把这段路的路面高度修成单调上升的或者单调下降的,整条路可以看成 N段,N个整数A1,-..,An(1<=n<=2000)依次描述了每一段路的高度(0<=Ai<=1000000000). 希望找到一个恰好含N个元素的不上升或不下降的序列B1,--,Bn,作为修过的路中每个路段的高度. 由于将每一段路垫高或挖低一个单位消耗的体力相同,于是可以表示为: |A1-B1|+|

1037. 二哥买草

Description 二哥在网上买干草.他发现了一笔特殊的买卖.他每买一捆大小为A(1 <= A <= 1,000,000)的干草,他就能免费获得一捆大小为B(1 <= B < A)的干草,也就是说免费的那个必须大小是小于购买的那个. 然而,这笔交易是有规定的:大的一捆干草必须是高质量的,小的一捆是低质量的.二哥是个吝啬鬼,他并不在意:随便什么质量,只要能省钱就好. 给出一组N(1 <= N <= 10,000)捆高质量干草的大小,M(1 <= M <=