(a) 目前使用的格里高利历闰年的规则如下:
1 2 3 4 1. 公元年分非4的倍数,为平年。 2. 公元年分为4的倍数但非100的倍数,为闰年。 3. 公元年分为100的倍数但非400的倍数,为平年。 4. 公元年分为400的倍数为闰年。
请用一个表达式判断某一年是否为闰年。
1 if (year % 4 == 0 && year % 100 != 0 || year % 400 ==0 )
(b) 输入某一天的年月日,输出下一天的年月日。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdbool.h> int daysOfMonth[13 ] = { 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 };bool isLeapYear (int year) ;void setFebDay () ;int main (void ) { int year, month, day; scanf ("%d %d %d" ,&year,&month,&day); setFebDay(year); day++; if (day > daysOfMonth[month]) { day = 1 ; month++; } if (month == 13 ) { month = 1 ; year ++; } printf ("year = %d, month = %d, day = %d\n" , year, month, day); return 0 ; } bool isLeapYear (int year) { return year % 4 == 0 && year % 100 != 0 || year % 400 == 0 ; } void setFebDay (int year ) { if (isLeapYear(year)) daysOfMonth[2 ] = 29 ; else daysOfMonth[2 ] = 28 ; }
(c) 输入某两天的年月日,输出这两天的相距多少天。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdbool.h> int daysOfMonth[13 ] = { 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 };bool isLeapYear (int year) ;void setFebDay () ;int distance (int year1, int month1, int day1, int year2, int month2, int day2) ;int main (void ) { int year1, month1, day1; scanf ("%d %d %d" ,&year1,&month1,&day1); int year2, month2, day2; scanf ("%d %d %d" , &year2, &month2, &day2); printf ("差距%d天" , distance(year1, month1, day1, year2, month2, day2)); return 0 ; } bool isLeapYear (int year) { return year % 4 == 0 && year % 100 != 0 || year % 400 == 0 ; } void setFebDay (int year ) { if (isLeapYear(year)) daysOfMonth[2 ] = 29 ; else daysOfMonth[2 ] = 28 ; } int distance (int year1, int month1, int day1, int year2, int month2, int day2) { int days = 0 ; setFebDay(year1); int distance_1 = 0 ; for (int m = 1 ; m < month1; m++) { distance_1 += daysOfMonth[m]; } distance_1 += day1; setFebDay(year2); int distance_2 = 0 ; for (int m = 1 ; m < month2; m++) { distance_2 += daysOfMonth[m]; } distance_2 += day2; int distance_3 = 0 ; for (int y = year1; y < year2; y++) { distance_3 += 365 ; if (isLeapYear(y)) distance_3 += 1 ; } days = distance_2 + distance_3 - distance_1; return days; }
这个的验证比较麻烦,毕竟自己不可能计算很多天数来跟程序的结果比对。我使用的excel自带的公式。方法在这里
如何计算两日期相差的天数、月数或年数?—DATEDIF函数 - 小崔学数据分析的文章 - 知乎https://zhuanlan.zhihu.com/p/441248425
将excel公式计算出的结果和程序的结果比对就知道程序是对的。
(d) 已知1970年1月1日是星期四,输入之后的某一天的年月日,判断它是星期几?
1 2 3 4 5 int weekday (int year, int month, int day) { return (4 + distance(1970 , 1 , 1 , year, month, day)) % 7 ; }
(e) 输入1970年之后任意一年的年份,输出该年的年历。对话如下:(拓展题,不要求每个同学作答)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdbool.h> int daysOfMonth[13 ] = { 0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 };const char * DayOfWeek[] = { "SUN" , "MON" , "TUE" , "WED" , "THU" , "FRI" ,"SAT" };bool isLeapYear (int year) ;int weekday (int year, int month, int day) ;void setFebDay (int year) ;int distance (int year1, int month1, int day1, int year2, int month2, int day2) ;void printCalendar (int year) ;int main (void ) { int year; printf ("Please input the year whose calendear you want to know?\n" ); scanf ("%d" , &year); printCalendar(year); return 0 ; } bool isLeapYear (int year) { return year % 4 == 0 && year % 100 != 0 || year % 400 == 0 ; } void setFebDay (int year ) { if (isLeapYear(year)) daysOfMonth[2 ] = 29 ; else daysOfMonth[2 ] = 28 ; } int distance (int year1, int month1, int day1, int year2, int month2, int day2) { int days = 0 ; setFebDay(year1); int distance_1 = 0 ; for (int m = 1 ; m < month1; m++) { distance_1 += daysOfMonth[m]; } distance_1 += day1; setFebDay(year2); int distance_2 = 0 ; for (int m = 1 ; m < month2; m++) { distance_2 += daysOfMonth[m]; } distance_2 += day2; int distance_3 = 0 ; for (int y = year1; y < year2; y++) { distance_3 += 365 ; if (isLeapYear(y)) distance_3 += 1 ; } days = distance_2 + distance_3 - distance_1; return days; } int weekday (int year, int month, int day) { return (4 + distance(1970 , 1 , 1 , year, month, day)) % 7 ; } void printCalendar (int year) { setFebDay(year); printf ("=====================The Calendar of Year %d====================|\n" , year); for (int i = 1 ; i <= 6 ; i++) { } }
判断素数的函数 C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <iostream> #include <cstring> #include <algorithm> #include <cmath> int main () { int n; scanf ("%d" , &n); int x; for (int i=1 ;i<=n;i++){ scanf ("%d" , &x); bool is_prime = true ; if (x == 2 ) is_prime = true ; else if (x % 2 ==0 ||x<2 ) is_prime = false ; else { for (int j = 3 ; j*j<=x;j+=2 ) { if (x%j == 0 ) { is_prime = false ; break ; } } } if (is_prime == true ) printf ("%d is prime\n" ,x); else printf ("%d is not prime\n" ,x); } return 0 ; }
C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #define _CRT_SECURE_NO_WARNINGS #define SIZE(a) (sizeof(a) / sizeof(a[0])) #include <stdio.h> #include <stdlib.h> #include <time.h> #include <stdbool.h> #define N 100 bool isPrime (int x) ;int main (void ) { int x; scanf ("%d" , &x); if (isPrime(x)) printf ("is prime\n" ); else printf ("is not prime\n" ); return 0 ; } bool isPrime (int x) { bool is_prime = true ; if (x == 2 ) is_prime = true ; else if (x % 2 == 0 || x < 2 ) is_prime = false ; else { for (int i = 3 ; i * i <= x; i += 2 ) { if (x % i == 0 ) { is_prime = false ; break ; } } } return is_prime; }
递归 从递归的定义去理解递归不是很好,我们可以从名字入手去理解。
递:把大问题分解成若干个子问题,子问题的求解方式和大问题一致,只是问题规模不一致。
归:把子问题的解合并成大问题的解。
例子1:电影院的例子。 例子2:Fibonacci 数列。 1 2 3 4 5 long long fib (int n) {if (n == 0 || n == 1 ) return n;return fib(n-2 ) + fib(n-1 );}
但是这种求解方式的效率很低,会存在大量重复的计算。如下图所示
so,如何避免重复计算问题呢?答案是动态规划。顺序求解子问题,并将子问题的解保存起来,从而避免重复计算,最终求解到大问题。
0 1 1 2 3 5 8
但是对于求解 Fibnacci 数列来说,我们并不需要保存前面所有项的值,我们只需要保存最近两项即可
1 2 3 4 5 6 7 8 9 10 11 long long fib (int n) {if (n == 0 || n == 1 ) return n;long long a = 0 , b = 1 ;for (int i = 2 ; i <= n; i++) {long long tmp = a + b;a = b; b = tmp; } return b;}
例子3:汉诺塔 有三根杆子A,B,C。A杆上有 N 个 (N>1) 穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至 C 杆:
每次只能移动一个圆盘;
大盘不能叠在小盘上面。
提示:可将圆盘临时置于 B 杆,也可将从 A 杆移出的圆盘重新移回 A 杆,但都必须遵循上述两条规则。
问:最少需要移动多少次?如何移?
(1) 输入:
n = 1
输出:
total step(s): 1
A –> C
(2) 输入:
n = 2
输出:
total step(s): 3
A –> B
A –> C
B –> C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #define _CRT_SECURE_NO_WARNINGS #define SIZE(a) (sizeof(a) / sizeof(a[0])) #include <stdio.h> #include <stdlib.h> #include <time.h> #include <stdbool.h> #include <math.h> #define N 100 void hanio_helper (int n, char c1, char c2, char c3) ;void hanio (int n) ;long long fib (int n) { if (n == 1 || n == 0 ) return n; long long a = 0 ; long long b = 1 ; for (int i = 2 ; i <= n; i++) { long long tmp = a + b; a = b; b = tmp; } return b; } int main (void ) { int n; scanf ("%d" , &n); hanio(n); return 0 ; } void hanio_helper (int n, char name1, char name2, char name3) { if (n == 1 ) { printf ("%c --> %c\n" , name1, name3); return ; } hanio_helper(n - 1 , name1, name3 , name2); printf ("%c --> %c\n" , name1 , name3); hanio_helper(n - 1 , name2, name1, name3); } void hanio (int n) { long long steps = (1 << n) - 1 ; printf ("total steps is: %lld\n" , steps); hanio_helper(n, 'A' , 'B' , 'C' ); }
例子4:约瑟夫环问题(每2人干掉一个) 约瑟夫环是一个数学的应用问题:已知 n 个人 (以编号1,2,3, …, n 分别表示) 围坐在一张圆桌周围。从编号为 1 的人开始,每两个人出列一个人,直至只剩一个人。问:最终剩下的这个人的编号是多少?
思路: 循环链表, 循环数组:
空间复杂度:O(n)
时间复杂度:O(n)
但如果用递归
空间复杂度:O(logn),因为栈的深度
时间复杂度:O(logn)
递推公式
joseph(n) = 2 * joseph(n / 2) - 1 n为偶数
joseph(n) = 2 * joseph((n - 1) / 2) + 1 n为奇数
边界条件
n == 1 return 1;joseph(1) = 1
n == 2 return 1;joseph(2) = 1
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <time.h> #include <stdbool.h> int joseph (int n) ;int main (void ) { int n; scanf ("%d" , &n); printf ("%d" , joseph(n)); return 0 ; } int joseph (int n) { if (n == 1 || n == 2 ) return 1 ; else if (n > 2 && n % 2 == 0 ) return 2 * joseph(n / 2 ) - 1 ; else return 2 * joseph((n - 1 ) / 2 ) + 1 ; }
例子5:约瑟夫环问题(每m人干掉一个) 循环链表
空间复杂度:O(n)
时间复杂度:O(m * n)
递推公式joseph(n, m) = (joseph(n - 1, m) + m) % n
边界条件joseph(1, m) = 0 // 这里的编号是从0到n - 1
空间复杂度:O(n)
时间复杂度:O(n)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <time.h> #include <stdbool.h> int joseph (int n, int m) ;int main (void ) { int n; int m; scanf ("%d%d" , &n, &m); printf ("%d" , 1 + joseph(n, m)); return 0 ; } int joseph (int n , int m) { if (n == 1 ) return 0 ; return (joseph(n - 1 , m) + m) % n; }
总结:递归三问 Q1:什么情况下可以考虑使用递归?
答:问题具有递归结构。(1)也就是说,大问题可以分解成若干个子问题,子问题的求解方式和大问题一致,只是问题规模不一致 。(2)子问题的解可以合并 成大问题的解。
Q2: 到底要不要使用递归?
答:如果不存在重复计算问题,且递归的层次不是很深时,就可以使用递归。
Q3: 如何写递归?
答:两步走。(1) 边界条件 (2) 递归公式
(拓展题,不要求每个同学都作答) 德州扑克:写一个程序循环读取 5 张手牌 (输入 0 结束程序),然后把手中的牌分为下面某一类:1.同花顺 2.四张 3.葫芦 (3 + 2) 4. 同花 5. 顺子 6.三张 7.两对 8. 一对 9.高牌。程序对话如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 Enter a card: 2s Enter a card: 5s Enter a card: 4s Enter a card: 3s Enter a card: 6s Straight flush Enter a card: 8c Enter a card: as Enter a card: 8c Duplicate card; ignored. Enter a card: 7c Enter a card: ad Enter a card: 3h Pair Enter a card: 6s Enter a card: d2 Bad card; ignored. Enter a card: 2d Enter a card: 9c Enter a card: 4h Enter a card: ts High card Enter a card: 0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 #include <stdio.h> #include <stdbool.h> void read_cards (void ) ;void analyze_hand (void ) ;void print_result (void ) ;int nums_in_suit[4 ], nums_in_rank[13 ];bool flush, straight, four, three;int pairs; int main (void ) { for (;; ) { read_cards(); analyze_hand(); print_result(); } } void print_result (void ) { if (flush && straight) printf ("Flush straight\n" ); else if (four) printf ("Four\n" ); else if (three && pairs) printf ("Full house\n" ); else if (flush) printf ("Flush\n" ); else if (straight) printf ("Straight\n" ); else if (three) printf ("Three\n" ); else if (pairs == 2 ) printf ("Two pairs\n" ); else if (pairs) printf ("One pair\n" ); else printf ("High card\n" ); printf ("\n" ); } void analyze_hand (void ) { flush = false ; straight = false ; four = false ; three = false ; pairs = 0 ; for (int i = 0 ; i < 4 ; i++) { if (nums_in_suit[i] == 5 ) { flush = true ; break ; } } int idx = 0 ; while (nums_in_rank[idx] == 0 ) idx++; int n_consective = 1 ; while (idx < 12 && nums_in_rank[++idx]) { n_consective++; } if (n_consective == 5 ) { straight = true ; return ; } for (int i = 0 ; i < 13 ; i++) { if (nums_in_rank[i] == 4 ) { four = true ; } else if (nums_in_rank[i] == 3 ) { three = true ; } else if (nums_in_rank[i] == 2 ) { pairs++; } } }
void read_cards() { // 初始化操作 bool in_hand[4][13] = { false };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 for (int i = 0 ; i < 13 ; i++) { nums_in_rank[i] = 0 ; } for (int i = 0 ; i < 4 ; i++) { nums_in_suit[i] = 0 ; } int cards_read = 0 ; while (cards_read < 5 ) { bool bad_card = false ; printf ("Enter a card: " ); char c = getchar(); int rank; switch (c) { case '0' : exit (0 ); case '2' : rank = 0 ; break ; case '3' : rank = 1 ; break ; case '4' : rank = 2 ; break ; case '5' : rank = 3 ; break ; case '6' : rank = 4 ; break ; case '7' : rank = 5 ; break ; case '8' : rank = 6 ; break ; case '9' : rank = 7 ; break ; case 't' :case 'T' : rank = 8 ; break ; case 'j' :case 'J' : rank = 9 ; break ; case 'q' :case 'Q' : rank = 10 ; break ; case 'k' :case 'K' : rank = 11 ; break ; case 'a' :case 'A' : rank = 12 ; break ; default : bad_card = true ; } c = getchar(); int suit; switch (c) { case 'd' :case 'D' : suit = 0 ; break ; case 'c' :case 'C' : suit = 1 ; break ; case 'h' :case 'H' : suit = 2 ; break ; case 's' :case 'S' : suit = 3 ; break ; default : bad_card = true ; } while ((c = getchar()) != '\n' ) { if (c != ' ' && c != '\t' ) bad_card = true ; } if (bad_card) { printf ("Bad card; ignored.\n" ); } else if (in_hand[suit][rank]) { printf ("Duplicate card; ignored.\n" ); } else { in_hand[suit][rank] = true ; cards_read++; nums_in_rank[rank]++; nums_in_suit[suit]++; } } }
编写程序模拟掷骰子的游戏(两个骰子)。第一次掷的时候,如 果点数之和为 7 或 11 则获胜;如果点数之和为2、3或12则落败;其他情况下的点数之和称为“目标”,游戏继续。在后续的投掷中,如果玩家再次掷出“目标”点数则获胜,掷出7则落败,其他情况都忽略,游戏继续进行。每局游戏结束时,程序询问用户是否再玩一次,如果用 户输入的回答不是 y 或 Y ,程序会显示胜败的次数然后终止。(拓展题,不要求每个同学作答)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 You rolled: 8 Your point is 8 You rolled: 3 You rolled: 10 You rolled: 8 You win! Play again? y You rolled: 6 Your point is 6 You rolled: 5 You rolled: 12 You rolled: 3 You rolled: 7 You lose! Play again? y You rolled: 11 You win! Play again? n Wins: 2 Losses: 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #include <stdio.h> #include <stdlib.h> #include <time.h> #include <stdbool.h> bool play_game () ;int roll_dice () ;int main (void ) { int wins = 0 , losses = 0 ; char again; do { bool result = play_game(); if (result) { wins++; } else { losses++; } printf ("\nPlay again?" ); again = getchar(); while (getchar() != '\n' ) ; } while (again == 'y' || again == 'Y' ); printf ("\nWins: %d, losses: %d\n" , wins, losses); } int roll_dice () { int r1 = rand() % 6 + 1 ; int r2 = rand() % 6 + 1 ; printf ("You rolled: %d\n" , r1 + r2); return r1 + r2; } bool play_game () { srand(time(NULL )); int tolly = roll_dice(); if (tolly == 7 || tolly == 11 ) { printf ("You win!\n" ); return true ; } if (tolly == 2 || tolly == 3 || tolly == 12 ) { printf ("You lose!\n" ); return false ; } int point = tolly; printf ("Your point is %d" , point); for (;;) { tolly = roll_dice(); if (tolly == point) { printf ("You win!\n" ); return true ; } if (tolly == 7 ) { printf ("You lose!\n" ); return false ; } } }