任務一:
#include <stdio.h> char score_to_grade(int score); int main() { int score; char grade; while(scanf("%d", &score) != EOF) { grade = score_to_grade(score); printf("分數: %d, 等級: %c\n\n", score, grade); } return 0; } char score_to_grade(int score) { char ans; switch(score/10) { case 10: case 9: ans = 'A'; break; case 8: ans = 'B'; break; case 7: ans = 'C'; break; case 6: ans = 'D'; break; default: ans = 'E'; } return ans; }
問題1:函式score_to_grade的功能是根據傳入的整數分數score,將其轉換為對應的等級字元並返回。形參型別是int,即接受一個整數引數。返回值型別是char,即返回一個字元。
問題2:有問題。問題在於當score/10的值為 9 或者 10 時,執行完ans = "A"後,沒有break語句,程式會繼續執行下一個case,同理對於其他的case也存在這個問題。這樣會導致最終返回的等級可能不是預期的結果。例如,如果score為 95,理論上應該返回等級A,但由於沒有break,程式會繼續執行後續的case,直到遇到break或者整個switch語句結束,這樣可能會返回B、C、D或E等錯誤的等級。此外,ans被定義為字元型別變數,而在修改後的程式碼中,給ans賦值用了雙引號括起來的字串常量,這是不匹配的型別賦值,在 C 語言中會導致編譯錯誤。
任務2:
#include <stdio.h> int sum_digits(int n); // 函式宣告 #include <stdio.h> int sum_digits(int n); // 函式宣告 int main() { int n; int ans; while(printf("Enter n: "), scanf("%d", &n) != EOF) { ans = sum_digits(n); // 函式呼叫 printf("n = %d, ans = %d\n\n", n, ans); } return 0; } // 函式定義 int sum_digits(int n) { int ans = 0; while(n != 0) { ans += n % 10; n /= 10; } return ans; }
問題 1:函式sum_digits的功能是計算給定整數n的各位數字之和並返回。
問題 2:可以實現同等效果。
兩種實現方式背後的演算法思維區別如下:
(1)第一種實現方式是採用迴圈的演算法思維。透過不斷地取n的末位數字累加到ans中,然後將n除以 10 去掉末位數字,重複這個過程直到n變為 0。這種方式是一種迭代的過程,逐步處理整數的每一位數字。
(2)第二種實現方式是採用遞迴的演算法思維。如果輸入的整數n小於 10,直接返回n,因為一個一位數的各位數字之和就是它本身。如果n大於等於 10,則先遞迴地計算n/10的各位數字之和,再加上n的末位數字n%10。這種方式是將問題不斷分解為更小的子問題,直到子問題可以直接求解,然後逐步合併子問題的解得到最終結果。
任務3:
#include <stdio.h> int power(int x, int n); // 函式宣告 int main() { int x, n; int ans; while(printf("Enter x and n: "), scanf("%d%d", &x, &n) != EOF) { ans = power(x, n); // 函式呼叫 printf("n = %d, ans = %d\n\n", n, ans); } return 0; } // 函式定義 int power(int x, int n) { int t; if(n == 0) return 1; else if(n % 2) return x * power(x, n-1); else { t = power(x, n/2); return t*t; } }
問題 1:函式power的功能是計算整數x的n次方並返回結果。
問題 2:函式power是遞迴函式。遞迴模式為:當n為 0 時,返回 1;當n為奇數時,返回x * power(x, n - 1);當n為偶數時,先計算t = power(x, n/2),然後返回t * t。
數學公式模型為
power(x, 0)= 1;
當n為奇數時,power(x, n)= x* power(x, n -1);
當n為偶數時,power(x, n)= power(x, n/2)* power(x, n/2)。
任務4:
#include <stdio.h> int is_prime(int n) { if (n <= 1) return 0; for (int i = 2; i * i <= n; i++) { if (n % i == 0) return 0; } return 1; } int main() { int count = 0; printf("100 以內的孿生素數:\n"); for (int i = 1; i < 100; i++) { if (is_prime(i) && is_prime(i + 2)) { printf("%d %d\n", i, i + 2); count++; } } printf("100 以內的孿生素數共有 %d 個\n", count); return 0; }
任務5:
#include <stdio.h> #include <stdlib.h> void hanoi(unsigned int n, char from, char temp, char to, int *count); void moveplate(unsigned int n, char from, char to, int *count); int main() { unsigned int n; while (scanf("%u", &n)!= EOF) { int count = 0; hanoi(n, 'A', 'B', 'C', &count); printf("一共移動了%d次。\n", count); } system("pause"); return 0; } void hanoi(unsigned int n, char from, char temp, char to, int *count) { if (n == 1) { moveplate(n, from, to, count); } else { hanoi(n - 1, from, to, temp, count); moveplate(n, from, to, count); hanoi(n - 1, temp, from, to, count); } } void moveplate(unsigned int n, char from, char to, int *count) { printf("%u:%c-->%c\n", n, from, to); (*count)++; }
任務6:
迭代
#include <stdio.h> int func_iterative(int n, int m) { if (m > n || m < 0) return 0; if (m == 0 || m == n) return 1; int result = 1; for (int i = 1; i <= m; i++) { result = result * (n - (m - i)) / i; } return result; } int main() { int n, m; int ans; while (scanf("%d%d", &n, &m)!= EOF) { ans = func_iterative(n, m); printf("n = %d, m = %d, ans = %d\n\n", n, m, ans); } return 0; }
遞迴
#include <stdio.h> int func_recursive(int n, int m) { if (m > n || m < 0) return 0; if (m == 0 || m == n) return 1; return func_recursive(n - 1, m - 1) + func_recursive(n - 1, m); } int main() { int n, m; int ans; while (scanf("%d%d", &n, &m)!= EOF) { ans = func_recursive(n, m); printf("n = %d, m = %d, ans = %d\n\n", n, m, ans); } return 0; }#include <stdio.h> #include <stdlib.h> int print_charman(int n); int main() { int n; printf("Enter n: "); scanf("%d", &n); print_charman(n); return 0; }
任務7:
int print_charman(int n){ for(int t=0;t<n;t++){ int i=(n-t)*2-1; int j=t; for(j;j>0;j--){ printf(" "); } for(i;i>0;i--){ printf(" O "); } printf("\n"); j=t; for(j;j>0;j--){ printf(" "); } i=(n-t)*2-1; for(i;i>0;i--){ printf("<H> "); } printf("\n"); j=t; for(j;j>0;j--){ printf(" "); } i=(n-t)*2-1; for(i;i>0;i--){ printf("I I "); } printf("\n"); } return 0; }
實驗總結
知識歸納:
1.函式宣告與定義:我學會了如何正確地宣告和定義函式,明確函式的輸入引數型別和返回值型別,確保在主函式中能夠正確呼叫。理解了函式宣告是為了讓編譯器提前知道函式的存在,而函式定義則是具體實現函式功能的程式碼部分。
2.迴圈結構:for
迴圈和while
迴圈的靈活運用。在列印字元小人陣列的程式中,使用for
迴圈控制行數和列數,精確地輸出特定格式的圖形。透過迴圈的條件判斷,如迴圈變數的起始值、終止值和步長的設定,實現不同的輸出效果。
3.遞迴與迭代:遞迴方式:理解了遞迴的概念和實現方法。在計算組合數和漢諾塔問題中,遞迴函式透過不斷呼叫自身來解決問題,將複雜問題分解為更小的子問題。但也需要注意遞迴的深度,避免棧溢位。
迭代方式:掌握了迭代的方法,透過迴圈和變數的更新來逐步計算結果。與遞迴相比,迭代通常更高效,尤其是在處理大規模資料時。
體會感受:
在解決不同問題時,需要根據問題的特點選擇合適的演算法。例如,計算組合數可以使用遞迴或迭代方式,兩種方法各有優缺點。遞迴方式程式碼簡潔,但可能會導致棧溢位;迭代方式效率高,但程式碼相對複雜一些。在列印字元小人陣列的問題中,透過分析圖形的規律,選擇合適的迴圈結構和條件判斷來實現輸出。透過遞迴方式解決問題,讓我深刻體會到了程式設計的簡潔之美。遞迴函式能夠將複雜問題分解為簡單的子問題,使程式碼更加清晰易懂。但同時也意識到遞迴的侷限性,如棧空間的限制和效能問題。