I am a teacher!

导航

C语言程序设计100例之(71):18岁生日

例71   18岁生日

问题描述

小明的18岁生日就要到了,他当然很开心,可是他突然想到一个问题,是不是每个人从出生开始,到达18岁生日时所经过的天数都是一样的呢?似乎并不全都是这样,所以他想请你帮忙计算一下他和他的几个朋友从出生到达18岁生日所经过的总天数,让他好来比较一下。

输入

一个数T,后面T行每行有一个日期,格式是YYYY-MM-DD。如我的生日是1988-03-07。

输出

T行,每行一个数,表示此人从出生到18岁生日所经过的天数。如果这个人没有18岁生日,就输出-1。

输入样例

1

1988-03-07

输出样例

6574

        (1)编程思路。

        设输入的出生日期为year年month月day日,则18岁生日是year+18年month月day日,若year+18年不是闰年,但month=2,day=29,则18岁生日不存在,输出-1。

        设所经过的天数为sum,初始时sum=0。每年至少有365天,18年,所以sum=sum+18*365;闰年有365天,因此在第year+1~year+17这17年中每个闰年加1天;若month>2,则第year+18年是闰年的话也要加1天;若month<2或者month==2但day!=29,则第year年是闰年的话也要加1天。将这些情况都考虑清楚不要遗漏。

(2)源程序。

#include <stdio.h>

int leapyear(int year)

{

    if (year%4==0 && year%100!=0 || year%400==0)

        return 1;

    else

        return 0;

}

int main()

{

    int t,year,month,day,sum;

    scanf("%d", &t);

    while(t--)

    {

        scanf("%d-%d-%d", &year, &month, &day);

        if (month == 2 && day == 29 && leapyear(year +18)!=1)

            printf("-1\n");

        else

        {

            sum = 365*18;

            for (int i=year+1; i<year+18; i++)

                sum +=  leapyear(i);

            if (month > 2)

                sum += leapyear(year + 18);

            else if (month<2 || day != 29)

                sum += leapyear(year);

            printf("%d\n", sum);

        }

    }

    return 0;

}

习题71

71-1  今夕何夕

问题描述

今天是2017年8月6日,农历闰六月十五。

小明独自凭栏,望着一轮圆月,发出了“今夕何夕,见此良人”的寂寞感慨。

为了排遣郁结,他决定思考一个数学问题:接下来最近的哪一年里的同一个日子,和今天的星期数一样?比如今天是8月6日,星期日。下一个也是星期日的8月6日发生在2023年。

输入

第一行为T(1 ≤ T ≤ 10000),表示输入数据组数。

每组数据包含一个日期,格式为YYYY-MM-DD。其中YYYY ≥ 2017,日期一定是个合法的日期。

输出

对每组数据输出答案年份,题目保证答案不会超过四位数。

输入样例

3

2017-08-06

2017-08-07

2018-01-01

输出样例

2023

2023

2024

        (1)编程思路。

        目前全世界通用的历法称为公历,它实质上是一种阳历。

        原始的阳历是古埃及人创立的。最初取一年为365日。为了协调历法年与回归年的长度,公元前46年罗马统治者儒略·凯撒对阳历作了修改,制定儒略历。公元前8年,凯撒的侄儿奥古斯都又对儒略历作为调整。儒略历分一年为十二个月,平年365日;年份能被4整除的为闰年,共366日。这样,儒略历历年平均长度便是365.25日,同回归年长度365.2422日相差0.07078日,400年约差3日。从实施儒略历到十六世纪末期,累差约为10日。为了消除了这个差数,教皇格里高利十三世把儒略历1582年10月4日的下一天定为10月15日,中间消去10天;同时还修改了儒略历置闰法则:能被4除尽的年份仍然为闰年,但对世纪年(如1600,1700,……),只有能被400除尽的才为闰年。这样,400年中只有97个闰年,比原来减少三个,使历年平均长度为365.2425日,更接近于回归年的长度。经过这样修改的儒略历叫格里高利历,亦称格里历。格里历二十世纪初为全世界普遍采用,所以又叫公历。

        如何确定一个给定的日期是星期几呢?

        设变量year、month和day分别表示给定日期的年、月和日。由于一个星期有7天,因此,如果我们知道该年的元旦是星期几(设为week),那么该日期是星期几就可以确定了,计算公式为:(week+d-1)%7。0代表星期日,其中d为给定日期是当年的第几天。

        怎样求年号为year这年的元旦是星期几呢?用一个简单公式即可计算出来。

        设公元 1 年 1月 1日 为星期一。

        我们知道一年有365天(当然闰年会有366天,先不管了),每7天1周,365%7=1。即若不考虑闰年,则每年的元旦是星期几应该是上一年元旦星期几的后一天。

       由于 1 年元旦是星期一,因此 2年元旦星期二,3年元旦星期三,4年元旦星期四,…即

           week =(year)%7。

        由于闰年的存在会在2月多一天,因此,闰年的下一年元旦星期几应再加1,即5年为星期六(不是星期五,因为 4年是闰年),因此需要知道前year-1年中有多少个闰年。闰年的规则简述就是每4年一个闰年,每100年不是闰年,每400年又是闰年。按集合包含与容斥规则,闰年个数有 (year-1)/4 - (year-1)/100 + (year-1)/400。

        因此,已知年号year,就可以根据year计算出该年元旦是星期几。计算公式如下:

         week=[ year+(year-1)/4-(year-1)/100+(year-1)/400]%7;

        需要说明的是,对于公元历任一年,这个公式不一定成立,因为由前面的介绍,公元历在历史上也不断调整过,但对于现行的公历,这一公式是可以正确确定某年元旦是星期几的。

        例如,year=2001,week=(2001+(2001-1)/4-(2001-1)/100+(2001-1)/400)%7

                     =(2001+500-20+5)%7=1

        即2001年元旦是星期一,正好符合。

        确定了每年元旦是星期几后,对之后的年份进行穷举,找到一个最近的日期,其星期相同即可。不考虑闰年的话,若某两年的元旦星期相同,则两年相同的日子的星期也相同;若考虑闰年,则对2月份以后的日子进行闰年处理。

        (2)源程序。

#include <stdio.h>

bool isLeap(int year)

{

    if (year%4==0 && year%100!=0 || year%400==0)

        return true;

    else

        return false;

}

int main()

{

    int year,month,day,t,week1,week2,i;

    scanf("%d",&t);

    while(t--)

    {

        scanf("%d-%d-%d",&year,&month,&day);

        week1=(year+(year-1)/4-(year-1)/100+(year-1)/400)%7;

        for (i=year+1;i<=9999;i++)

        {

            if (month==2&&day==29&&!isLeap(i))

                continue;

            week2=(i+(i-1)/4-(i-1)/100+(i-1)/400)%7;

            if(month>2)

            {

                if (isLeap(year) && ! isLeap(i))

                    week2=(week2-1+7)%7;

                if (!isLeap(year) && isLeap(i))

                    week2=(week2+1)%7;

            }

            if (week1==week2)

            {               

                printf("%d\n",i);

                break;

            }

        }

    }

    return 0;

}

71-2  第N个闰年

问题描述

给定一个表示起始年份的正整数Y和一个正整数N,你的任务是求出从Y年开始的第N个闰年。

注:如果Y年是闰年,那么第一个闰年就是Y年。

输入

输入包含几个测试用例。输入的第一行是一个整数T,它是测试用例的数量。

每个测试用例包含两个正整数Y和N(1<=N<=10000)。

输出

对于每个测试用例,您应该输出从Y年开始的第N个闰年。

输入样例

3

2005 25

1855 12

2004 10000

输出样例

2108

1904

43236

        (1)编程思路。

        编写函数int leapyear(int year)判断year年是否是闰年。为求从Y年开始的第N个闰年,进行简单的循环处理即可。

       (2)源程序。

#include <stdio.h>

int leapyear(int year)

{

    if (year%4==0 && year%100!=0 || year%400==0)

        return 1;

    else

        return 0;

}

int main()

{

    int t;

    scanf("%d", &t);

    while(t--)

    {

        int year,n;

              scanf("%d%d",&year,&n);

        while (leapyear(year)==0) year++;

        int cnt=1;

        while (cnt<n)

        {

            year+=4;

            if (leapyear(year)==1) cnt++;

        }

              printf("%d\n",year);

    }

    return 0;

}

71-3  回文日期

问题描述

在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。

小明习惯用8位数字表示一个日期,其中,前4位代表年份,接下来2位代表月份,最后2位代表日期。显然:一个日期只有一种表示方法,而两个不同的日期的表示方法不会相同。

小明认为,一个日期是回文的,当且仅当表示这个日期的8位数字是回文的。现在,小明想知道:在他指定的两个日期之间包含这两个日期本身,有多少个真实存在的日期是回文的。

一个8位数字是回文的,当且仅当对于所有的i (1≤i≤8)从左向右数的第i个数字和第9−i个数字(即从右向左数的第i个数字)是相同的。

例如:

•对于2016年11月19日,用8位数字20161119表示,它不是回文的。

•对于2010年1月2日,用8位数字20100102表示,它是回文的。

•对于2010年10月2日,用8位数字20101002表示,它不是回文的。

每一年中都有12个月份:

其中,1,3,5,7,8,10,12月每个月有31天;4,6,9,11月每个月有30天;而对于2月,闰年时有29天,平年时有28天。

输入

两行,每行包括一个8位数字。

第一行表示小明指定的起始日期date 1。

第二行表示小明指定的终止日期date 2。

输入数据保证都是真实存在的日期,且年份部分一定为4位数字,且首位数字不为0。

保证date1 —定不晚于date2。

输出

一个整数,表示在date1和date2之间,有多少个日期是回文的。

输入样例

20000101

20101231

输出样例

2

        (1)编程思路。

        采用字符串输入两个给定的日期date1和date2,然后分离出这两个日期的年份year1和year2。由于一年中最多只可能有一个回文日期,例如,若年份为abcd,则该年的回文日期可能为abcddcba,若dc在01~12之间,bc在01~XX(XX为dc月最多的天数),则abcddcba才是合法的回文日期。

        因此,对year1和year2之间的年份进行穷举,对每个枚举到的年份,判断该年可能的回文日期是否合法,同时对于第year1年,可能的回文日期不能小于date1,第year2年,可能的回文日期不能大于date2,条件均满足,则是一个回文日期,计数。

      (2)源程序。

#include <stdio.h>

int main()

{

    int table[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};

    char date1[9],date2[9];

    scanf("%s%s",date1,date2);

    int year1=0,year2=0,i;

    int beginmd=0,endmd=0;

    for (i=0;i<4;i++)

    {

        year1=year1*10+date1[i]-'0';

        year2=year2*10+date2[i]-'0';

        beginmd=beginmd*10+date1[i+4]-'0';

        endmd=endmd*10+date2[i+4]-'0';

    }

    int cnt=0;

    for (i=year1;i<=year2;i++)

    {

        if ((i%4==0 && i%100!=0) || i%400==0)

            table[2]=29;

        else

            table[2]=28;

        int month=(i%10)*10+(i/10)%10;

        if (month<1 || month>12) continue;

        int day=i/1000+((i/100)%10)*10;

        if (day<1 || day>table[month]) continue;

        if (i==year1 && month*100+day<beginmd) continue;

        if (i==year2 && month*100+day>endmd) continue;

        cnt++;

    }

     printf("%d\n", cnt);

     return 0;

}

posted on 2022-03-06 11:54  aTeacher  阅读(450)  评论(0编辑  收藏  举报