NJUPT ACM 合法日期

本文介绍了一种用于验证日期合法性的算法,使用C++和C语言实现,并详细解释了基姆拉尔森计算公式用于判断星期几的方法。

合法日期问题

一、   课题概述

知道日期的来历吗。我们通常以公元纪年,如公元2009年。

这种国际通行的纪年体系以传说中耶稣基督的生年为公历元年(相当于中国西汉平帝元年)。一年包括12个月,每个月的天数不全相同。

星期的起源应该是连系著月亮的周期,因为七天大约是月亮一周的四份之一。

Sunday、Monday、Tuesday、Wednesday、Thursday、Friday、Saturday依次表示星期日、星期一、星期二、星期三、星期四、星期五、星期六。

给定一个日期,判断是否合法。。

输入

对于每个测试用例:

输出YES或NO,判断y、m、d、w是否构成一个合法的日期。

二、   设计与实现

此题目对日期的合理性和所读入的星期进行判断,并进行相应的输出。

对日期规则设定利用if语句将所有不合法的范围列出并返回false。

计算星期几则利用基姆拉尔森计算公式W=(d+2*m+3*(m+1)/5+y+y/4-y/100+y/400+1)%7

公式中d表示日期中的日数,m表示月份数,y表示年数。得到的值加1则是对应的星期。注意:在公式中有个与其他公式不同的地方:把一月和二月看成是上一年的十三月和十四月例:如果是2014-1-1则换算成:2013-13-1来代入公式计算,得W=2,W++,则对应周三。

定义bool back函数返回日期是否合法,若合法返回true,否则返回false。

Main函数得到返回值并进行相应的输出。

 

C++代码耗时约200Ms,但是C语言写的代码耗时约15Ms,于是代码都附上

以下分别为用C++和C语言实现具体代码:

C++:

#include<iostream>

using namespace std;

#include<string>

bool back(inty,int m,int d,charweek[])//boolback函数判断是否满足合法日期条件

{

    if (m > 12 ||m <= 0 || y <= 0 ||d <= 0) return false;//此行及以下几行,当读入的y,m,d不符合日期的规则时return false

    if (m == 12 &&d> 31) return false;

    if (m == 11 &&d> 30) return false;

    if (m == 10 &&d> 31) return false;

    if (m == 9 &&d> 30) return false;

    if (m == 8 &&d> 31) return false;

    if (m == 7 &&d> 31) return false;

    if (m == 6 &&d> 30) return false;

    if (m == 5 &&d> 31) return false;

    if (m == 4 &&d> 30) return false;

    if (m == 3 &&d> 31) return false;

    if (y % 4 != 0 || (y % 100 == 0 &&y %400 != 0))//此行判断是否闰年,以便对2月天数的限制

    {

        if (m ==2 &&d > 28) return false;

    }

    else

    {

        if (m ==2 &&d > 29) return false;

    }

    if (m == 1 &&d> 31) return false;

 

    if (m == 1 ||m == 2)//判断星期用基姆拉尔森计算公式W=(d+2*m+3*(m+1)/5+y+y/4-y/100+y/400+1)%7,得到的值加一则是对应的星期

    {

        m +=12;//基姆拉尔森计算公式把一月和二月须看成是上一年的十三月和十四月

        y--;

    }

    switch (((d + 2 *m + 3 * (m + 1) / 5 +y + y / 4 - y / 100 + y / 400) % 7) + 1)

    {

    case 6:

        if(strcmp(week,"Saturday") == 0)break;

        return false;

        //字符串的比较函数一般形式:strcmp(字符串1,字符串2)

        //当s1<s2时,返回为负数

        //当s1= s2时,返回值 = 0

        //当s1>s2时,返回正数

    case 5:

        if(strcmp(week,"Friday") == 0)break;

            return false;

    case 4:

        if(strcmp(week,"Thursday") == 0)break;

            return false;

    case 3:

        if(strcmp(week,"Wednesday") == 0)break;

            return false;

    case 2:

        if(strcmp(week,"Tuesday") == 0)break;

            return false;

    case 1:

        if(strcmp(week,"Monday") == 0)break;

            return false;

    case 7:

        if(strcmp(week,"Sunday") == 0)break;

            return false;

    }

    return true;

}

 

int main()

{

    int n; //定义的测试次数

int y,m, d; //年月日

 

    cin >> n;

    char week[10]; //星期几的字符串

    while (n--)

    {

        cin >> y >> m >> d;

        cin >> week;

        if(back(y,m,d,week)==true)//判断返回值并进行相应的输出

            cout << "YES"<< endl;

        else

            cout << "NO"<< endl;

    }

    return 0;

}

 

C:

#include<stdio.h> 

#include<string.h> 

 

bool back(inty, int m, int d, char w[])  //判断是否合法函数

{

    if (m > 12 ||m <= 0 || y <= 0 ||d <= 0) return false//对日期合理性进行判断

    if (m == 12 &&d> 31) return false;

    if (m == 11 &&d> 30) return false;

    if (m == 10 &&d> 31) return false;

    if (m == 9 &&d> 30) return false;

    if (m == 8 &&d> 31) return false;

    if (m == 7 &&d> 31) return false;

    if (m == 6 &&d> 30) return false;

    if (m == 5 &&d> 31) return false;

    if (m == 4 &&d> 30) return false;

    if (m == 3 &&d> 31) return false;

    if (y % 4 != 0 || (y % 100 == 0 &&y %400 != 0))  //闰年特殊情况

    {

        if (m ==2 &&d > 28) return false;

    }

    else

    {

        if (m ==2 &&d > 29) return false;

    }

    if (m == 1 &&d> 31) return false;

    if (m == 1 ||m == 2)

    {

        m +=12;

        y--;

    }

    switch ((d + 2 *m + 3 * (m + 1) / 5 +y + y / 4 - y / 100 + y / 400) % 7 + 1)  //基姆拉尔森计算公式W+1

    {

    case 1:

        if(strcmp(w,"Monday") != 0) return false;

        break;

    case 2:

        if(strcmp(w,"Tuesday") != 0) return false;

        break;

    case 3:

        if(strcmp(w,"Wednesday") != 0) return false;

        break;

    case 4:

        if(strcmp(w,"Thursday") != 0) return false;

        break;

    case 5:

        if(strcmp(w,"Friday") != 0) return false;

        break;

    case 6:

        if(strcmp(w,"Saturday") != 0) return false;

        break;

    case 7:

        if(strcmp(w,"Sunday") != 0) return false;

        break;

    }

 

    return true;

}

 

int main()

{

    int n, y, m, d;

    char w[10];

    scanf("%d",&n);

    while (n--)

    {

        scanf("%d%d%d%s", &y, &m, &d, w);

        if(back(y, m, d, w) == true)

            printf("YES\n");

        elseprintf("NO\n");

    }

    return 0;

}

 

 

三、  测试数据和结果分析

 

输入5//测试次数
2009 5 29 Monday//年月日星期
2009 5 30 Saturday
2007 2 29 Sunday
2014 11 14 Tuesday
2014 11 16 Sunday

输出NO
YES
NO
NO
YES

 

 

 



四、总结

1.这个ACM题目倒也不是很难,虽然Runtime Error at Test 1(ACCESS_VIOLATION)

但是经过调试发现是判断语句格式的问题,虽然第一天便在 virtual studio 完美运行,但是是ACM系统给出的测试中报错Wrong Answer at Test 1,便觉得奇怪,修改了算法,还是不行,于是确定是系统输入输出先后问题。

 

我拿到题目时候,看到的是先输入测试次数n 然后输入n组数据,然后电脑程序处理,最后一起集中输出。这种程序要求的实现我用的是定义一个动态数组int *temp = new int[n];将每次循环返回的参数放到一个数组里面,最后遍历数组,进行相应的输出。

 

但是这个题目真正要求的是进行分散处理,也就是说,输入一组数据,进行处理输出。这样比较简单,不存在存储的问题。但是题目要求写的不太清楚,是我反复检查了自己的代码,添加了bool back函数,而不是如代码下面所示有太多的输出累赘语句,至少修改之后看上去比较简洁,也容易理解。

 

2. 利用调用函数的到函数值并进行相应的操作。我习惯于用C++进行码代码,但是C++编译在某处出现问题,于是我试着用C语言写了一个,试着提交通过了,于是我就跟踪C++些的代码,调试了好多次,最后发现问题出在case语句以及virtual studio和G++编译差异引起的

3.虽然正确答案没能用到其中处理其中输出方法,但是我了解了C++和C动态数组的申请与释放,C++用new申请,delect来释放

//C++中用指针p指向new动态分配的长度为len*sizeof(int)的内存空间

// int  *p=new   int[len];

// delete[]   p;

而C语言用内存空间函数malloc和calloc申请,free(void*ptr)进行释放,而且函数调用分别必须包含头文件malloc.h和calloc。

 

4.此程序运用了大量的if和if else语句用来判断每个细节的合法性。通过这道题我更深入掌握到了if 语句和break语句,以及strcmp(s1,s2)字符串比较函数和相关函数,及C++ 和C字符串的区别和联系。

 

一.数论 4 1.阶乘最后非零位 4 2. 模线性方程(组) 4 3. 素数表 6 4. 素数随机判定(miller_rabin) 6 5. 质因数分解 7 6. 最大公约数欧拉函数 8 二.图论_匹配 9 1. 二分图最大匹配(hungary邻接表形式) 9 2. 二分图最大匹配(hungary邻接表形式,邻接阵接口) 10 3. 二分图最大匹配(hungary邻接阵形式) 10 4. 二分图最大匹配(hungary正向表形式) 11 5. 二分图最佳匹配(kuhn_munkras邻接阵形式) 11 6. 一般图匹配(邻接表形式) 12 7. 一般图匹配(邻接表形式,邻接阵接口) 13 8. 一般图匹配(邻接阵形式) 14 9. 一般图匹配(正向表形式) 15 三.图论_生成树 16 1. 最小生成树(kruskal邻接表形式) 16 2. 最小生成树(kruskal正向表形式) 17 3. 最小生成树(prim+binary_heap邻接表形式) 19 4. 最小生成树(prim+binary_heap正向表形式) 20 5. 最小生成树(prim+mapped_heap邻接表形式) 21 6. 最小生成树(prim+mapped_heap正向表形式) 22 7. 最小生成树(prim邻接阵形式) 23 8. 最小树形图(邻接阵形式) 24 四.图论_网络流 25 1. 上下界最大流(邻接表形式) 25 2. 上下界最大流(邻接阵形式) 26 3. 上下界最小流(邻接表形式) 27 4. 上下界最小流(邻接阵形式) 29 5. 最大流(邻接表形式) 30 6. 最大流(邻接表形式,邻接阵接口) 31 7. 最大流(邻接阵形式) 32 8. 最大流无流量(邻接阵形式) 32 9. 最小费用最大流(邻接阵形式) 33 五. 图论_最短路径 34 1. 最短路径(单源bellman_ford邻接阵形式) 34 2. 最短路径(单源dijkstra_bfs邻接表形式) 35 3. 最短路径(单源dijkstra_bfs正向表形式) 35 4. 最短路径(单源dijkstra+binary_heap邻接表形式) 36 5. 最短路径(单源dijkstra+binary_heap正向表形式) 37 6. 最短路径(单源dijkstra+mapped_heap邻接表形式) 38 7. 最短路径(单源dijkstra+mapped_heap正向表形式) 39 8. 最短路径(单源dijkstra邻接阵形式) 40 9. 最短路径(多源floyd_warshall邻接阵形式) 40 六. 图论_连通性 41 1. 无向图关键边(dfs邻接阵形式) 41 2. 无向图关键点(dfs邻接阵形式) 42 3. 无向图块(bfs邻接阵形式) 43 4. 无向图连通分支(bfs邻接阵形式) 43 5. 无向图连通分支(dfs邻接阵形式) 44 6. 有向图强连通分支(bfs邻接阵形式) 44 7. 有向图强连通分支(dfs邻接阵形式) 45 8. 有向图最小点基(邻接阵形式) 46 七. 图论_应用 46 1.欧拉回路(邻接阵形式) 46 2. 前序表转化 47 3. 树的优化算法 48 4. 拓扑排序(邻接阵形式). 49 5. 最佳边割集 50 6. 最佳顶点割集 51 7. 最小边割集 52 8. 最小顶点割集 53 9. 最小路径覆盖 55 八. 图论_NP搜索 55 1. 最大团(n小于64)(faster) 55 2. 最大团 58 九. 组合 59 1. 排列组合生成 59 2. 生成gray码 60 3. 置换(polya) 61 4. 字典序全排列 61 5. 字典序组合 62 6. 组合公式 62 十. 数值计算 63 1. 定积分计算(Romberg) 63 2. 多项式求根(牛顿法) 64 3. 周期性方程(追赶法) 66 十一. 几何 67 1. 多边形 67 2. 多边形切割 70 3. 浮点函数 71 4. 几何公式 76 5. 面积 78 6. 球面 79 7. 三角形 79 8. 三维几何 81 9. 凸包(graham) 89 10. 网格(pick) 91 11. 圆 92 12. 整数函数 94 13. 注意 96 十二. 结构 97 1. 并查集 97 2. 并查集扩展(friend_enemy) 98 3. 堆(binary) 98 4. 堆(mapped) 99 5. 矩形切割 99 6. 线段树 100 7. 线段树扩展 102 8. 线段树应用 105 9. 子段和 105 10. 子阵和 105 十三. 其他 106 1. 分数 106 2. 矩阵 108 3. 日期 110 4. 线性方程组(gauss) 111 5. 线性相关 113 十四. 应用 114 1. joseph 114 2. N皇后构造解 115 3. 布尔母函数 115 4. 第k元素 116 5. 幻方构造 116 6. 模式匹配(kmp) 118 7. 逆序对数 118 8. 字符串最小表示 119 9. 最长公共单调子序列 119 10. 最长子序列 120 11. 最大子串匹配 121 12. 最大子段和 122 13. 最大子阵和 123
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值