PTA-基礎編程題目集


目錄

Memory Dot 我的個人博客,歡迎來玩。

Function

Link: PTA

6-1 簡單輸出整數 (10 分)

本題要求實現一個函數,對給定的正整數N,打印從1到N的全部正整數。

void PrintN ( int N ){
  for(int i = 1; i<=N; i++)
    printf("%d\n",i); 
}

思路:
題目很簡單但是試了好多次才通過,原因是把main函數和PrintN一起提交了,
事實上只用提交自己編寫的PrintN函數就可以了,不用提交main函數。



6-2 多項式求值 (15 分)

本題要求實現一個函數,計算階數為n,系數為a[0] ... a[n]的多項式在x點的值。

double f( int n, double a[], double x ){
  double sum;
  double x0 = 1; //x0必為 1
  for (int i=0; i <= n; i++){
    	if (i == 0);//i = 0即是x0 = 1,無操作
      else x0 *= x; //隨着循環逐個疊加x的次冪
      sum += a[i] * x0;//求和
    }
  return sum;
}

思路:
一開始的思路用了2個for循環,提交后提示復雜度O(n^2)不行。於是,github里查起答案:x0可以在1個循環里逐個疊加



6-3 簡單求和 (10 分)

本題要求實現一個函數,求給定的N個整數的和。

int Sum ( int List[], int N ){
  int sum = 0;
  for(int j = 0; j<N; j++)
    sum += List[j];
  return sum;
}

思路:
很簡單的一道題,但是還是提交了幾次。原因是:sum、j必須的賦初值0。PTA里,系統不會自動賦值0。



6-4 求自定類型元素的平均 (10 分)

本題要求實現一個函數,求N個集合元素S[]的平均值,其中集合元素的類型為自定義的ElementType。

ElementType Average( ElementType S[], int N ){
  ElementType sum = 0;
  for(int i = 0; i < N; i++){
    sum += S[i];
  }
  return sum/(ElementType)N; //N原來是int型,要強制轉換為ElementType
}

思路:
被除的N要強制轉換為ElementType,讓結果輸出為浮點數



6-5 求自定類型元素的最大值 (10 分)

本題要求實現一個函數,求N個集合元素S[]的平均值,其中集合元素的類型為自定義的ElementType。

ElementType Max( ElementType S[], int N ){
  ElementType max = S[0];
  for(int i = 0; i < N; i++){
    max = max > S[i] ? max : S[i];
  }
  return max;
}

思路:
要額外考慮到max負數的情況,故max初值不能賦值0,要賦值S[0



6-6 求單鏈表結點的階乘和 (15 分)

本題要求實現一個函數,求單鏈表L結點的階乘和。這里默認所有結點的值非負,且題目保證結果在int范圍內

int FactorialSum( List L ){ //return語句,輸出輸出!!
  int sum = 0;
  int num;
  while (L != NULL){// L-Next指向NULL時,會把其地址給L,所以得判斷L的地址
    for (num = 1 ;L->Data >  0 ;L->Data--)
      num *= L->Data;
    sum += num;
    L = L->Next;
  }
  return sum;
}

思路:
一開始運用時,一直在對L->Data重復賦值,導致這道題思路錯誤。而后,引入num才計算出來。目前會經常忘記return語句,得讓自己多注意。



6-7 統計某類完全平方數 (20 分)

本題要求實現一個函數,判斷任一給定整數N是否滿足條件:它是完全平方數,又至少有兩位數字相同,如144、676等。

int IsTheNumber ( const int N )
{
    int res = N; //讓N可以改變,因為N原來是const
    int num[10] = {0};//存放0 ~ 9的計數值
    if((int)sqrt(N) == sqrt(N)){ //或者用:sqrt(N) * sqrt(N) == N,來判斷是否為完全平方數
        while(res != 0){
            num[res % 10] ++;//對0 ~ 9某個計數
            res /= 10;//從十位、百位...開始逐個提取
        }
        for (int i = 0; i < 10; i ++ ){
            if(num[i] > 1) return 1;//0 ~ 9某個計數值大於等於2即該數符合要求
        }
    }
    return 0;
}

思路:
用數組num[10]分別計數0 ~ 9,具體要使用取余(%10),除法(/10)計數提取數值



6-8 簡單階乘計算 (10 分)

其中N是用戶傳入的參數,其值不超過12。如果N是非負整數,則該函數必須返回N的階乘,否則返回0

int Factorial( const int N ){
    int i;
    int fac = 1;
    if (N < 0) return 0;
    else 
        for(i = 1; i <= N; i++){
            fac *= i;
    }
    return fac;
}

思路:
本題沒有考慮N > 12的情況,即超出32位Tmax的情況。6-10會考慮。



6-9 統計個位數字 (15 分)

本題要求實現一個函數,可統計任一整數中某個位數出現的次數。例如-21252中,2出現了3次,則該函數應該返回3

int Count_Digit ( const int N, const int D ){
    int n = N;
    int num[10] = {0};
    if (n < 0) n = ~n+1; //負數取補碼轉為正數,但Tmin仍為Tmin,故單獨討論
    if (n == 0x80000000){//Tmin =0x80000000 = -2147483648
        if(D == 1 | D == 2 | D == 3 | D == 6 | D == 7) return 1;
        if(D == 4) return 3;
        if(D == 8) return 2;
    }
    if (n == 0) {// 0在下面的模塊中無法判斷,所以單獨判斷
        if(D == 0) return 1;    
        else return 0;
    }
    else
        while (n){    //參考6-7,提取並計數,但此模塊只適用於不是零的正整數(因為自加符號適用於正數計數)
            num[n % 10]++;
            n /= 10;
        }
    return num[D];
}

思路:
分類討論。1、n == 0x80000000n == 0,注意要使用==等價符號;2、將負數轉為正數處理。



6-10 階乘計算升級版 (20 分)

本題要求實現一個打印非負整數階乘的函數。(最大為1000!)

void Print_Factorial(const int N) {
    long sum = 1;
    if (N >= 0 && N <= 12) { //Tmax是9位數,12!也為9位數,所以可以直接計算
        for (int i = 0; i <= N; i++) {
            if (i == 0) {
                sum = 1;
            }
            else {
                sum = sum*i;
            }
        }
        printf("%d\n", sum);
    }
    else if (N > 12 && N <= 1000) {//大於12不能直接計算,用數組表示
        int Num[3000] = { 0 };  //確保最終結果的數組足夠大:1 ~ 9相乘最多有1*9=9位,10 ~ 99(90個兩位數)相乘最多有2*90=180位,100 ~ 999(90個三位數)相乘最多有3*900=2700位,1000是4*1=4位,總計2893,所以取整取3000位
        int i, j, k, n;
        k = 1;  //位數
        n = 0;  //進位
        Num[0] = 1;   //將結果先初始化為1
        int temp;  //階乘的任一元素與臨時結果的某位的乘積結果
        //計算
        for (i = 2; i <= N; i++){  //開始階乘,階乘元素從2開始,因為1*2=2;模擬乘法,將臨時結果的每位與階乘元素相乘,並更新
            for (j = 0; j < k; j++){ //實現乘法並提取進位值
                temp = Num[j] * i + n;  //相應階乘中的一項與當前所得結果的某位相乘,並加上進位
                Num[j] = temp % 10;  //更新結果的位上信息
                n = temp / 10;   //提取進位值
            }
            while (n != 0){ //如果有進位,更新最新位的信息,並擴展最終數值的位數(k)
                Num[k] = n % 10;  //新加一位,更新結果的位上信息。
                k++; //位數增1
                n = n / 10;   //判斷還能不能進位,若能,則繼續更新;若不能,則將n置0
            }
        }
        //輸出
        for (i = k - 1; i >= 0; i--){//從最后一位開始倒着輸出
            printf("%d", Num[i]);  
        }
        printf("\n");
    }
    else {
        printf("Invalid input\n");
    }
}

思路:
大於9位時,int無法表示,1000!的數值過於巨大,故采用數組Num[3000]表示。簡述:模擬乘法,存儲進位值,根據是否有進位擴展數組。
參考:
Link:參考答案



6-11 求自定類型元素序列的中位數 (25 分)

本題要求實現一個函數,求N個集合元素A[]的中位數,即序列中第⌊(N+1)/2⌋大的元素。其中集合元素的類型為自定義的ElementType

ElementType Median( ElementType A[], int N )
{
    int incr = N;
	int i, j, k;
	do{ 
		incr = incr / 2; //確定增量
		for (i = 0; i < incr; i++){ //實現分組
			for (j = i + incr; j < N; j += incr){ 
				if (A[j] < A[j - incr]){ //實現比較並交換
					ElementType temp = A[j];
					for (k = j - incr; k >= 0 && temp < A[k]; k -= incr){//大於兩個元素比較時使用
						A[k + incr] = A[k];
					}
					A[k + incr] = temp;
				}
			}
		}
	} while (incr > 1);
	return A[N / 2];//求N個集合元素A[]的中位數,即序列中第((N+1)/2向下取整)大的元素
}

思路:
使用希爾算法,時間復雜度為O(n^(1.3 ~ 2))。而選擇算法、冒泡算法的時間復雜度為O(n^2)不符合題目時間要求。
參考:
Link:希爾算法java實現圖示參考答案1參考答案2



6-12 判斷奇偶性 (10 分)

本題要求實現判斷給定整數奇偶性的函數。

int even( int n ){
    if (n % 2) return 0;
    else return 1;
}

思路:
n % 2 若有余數為偶數;若無余數即0為奇數



6-13 折半查找 (15 分)

給一個嚴格遞增數列,函數int Search_Bin(SSTable T, KeyType k)用來二分地查找k在數列中的位置。

int  Search_Bin(SSTable T, KeyType k){ //題目強制要求用二分法,不能用for循環歷遍
    int start = 0, end = T.length - 1, mid;
    while(start <= end){
        mid = (start + end)/2;
        if(T.R[mid].key == k) return mid;//T.R[].key范圍為T.R[1].key ~ T.R[end].key
        else if(T.R[mid].key > k) end = mid - 1; //若k值小於mid元素,則mid在遞增數列左邊,所以對end取值為mid - 1縮小范圍於遞增數列左邊
        else start = mid + 1;//若k值大於mid元素,則mid在遞增數列右邊,所以對start取值為mid + 1縮小范圍於遞增數列右邊
    }
    return 0;
}

思路:
雖然沒學過c++,但此函數幾乎完全用c寫。除了特殊的數據元素表示。簡述:首尾下標相加除以2確定中位數下標mid,然后根據中位數值與首尾數值的大小比較縮小范圍。(此處的中位數不是嚴格意義上的中位數,若start + end奇數,則會向下取整

函數部分完成時間:2021.9.22

Programming

7-1 厘米換算英尺英寸 (15 分)

如果已知英制長度的英尺foot和英寸inch的值,那么對應的米是(foot+inch/12)×0.3048。現在,如果用戶輸入的是厘米數,那么對應英制長度的英尺和英寸是多少呢?別忘了1英尺等於12英寸。

#include <stdio.h>

float c2f(int cm);
int getinch (int cm);

int main(){
	int cm;
	scanf("%d", &cm);
	printf("%d %d", (int)c2f(cm), getinch(cm));
	return 0;
}

float c2f (int cm){
	float m = cm / 100.0; //100必須是float即100.0,否則cm / 100為int/int是整型輸出,直接按float輸出,會輸出0
	float foot = m / 0.3048;//14 ~ 15也可以寫成 float m = cm / 30.48
	return foot;
}

int getinch (int cm){
	return (c2f(cm) - (int)c2f(cm)) * 12; //英寸取整乘以12
}

思路:int/int直接按float輸出,會輸出0。解決方案:將分子、分母任意一個值改成float



7-2 然后是幾點 (15 分)

有時候人們用四位數字表示一個時間,比如1106表示 11點零6分。現在,你的程序要根據起始時間和流逝的時間計算出終止時間。

讀入兩個數字,第一個數字以這樣的四位數字表示當前時間,第二個數字表示分鍾數,計算當前時間經過那么多分鍾后是幾點,結果也表示為四位數字。當小時為個位數時,沒有前導的零,例如530分表示為530030分表示為030。注意,第二個數字表示的分鍾數可能超過60,也可能是負數。

#include<stdio.h>

int main() {
	int a, b, Mul60, h, min;
	scanf("%d %d", &a, &b);
	Mul60 = (a / 100) * 60 + (a % 100) + b; // (a / 100)取a的小時部分,並令其為60的倍數;同時(a % 100)取a的分鍾部分,最后加上b
	h = Mul60 / 60; //提取小時
	min = Mul60 % 60;//提取分鍾
	
	//%d表示打印整型的,
	//%2d表示把整型數據打印最低兩位,
	//%02d表示把整型數據打印最低兩位,如果不足兩位,用0補齊
	printf("%d%02d",h,min);
    return 0;
}

思路:因為60進制轉化為60的倍數,並且分為hmin兩段輸出,注意要使用%02d



7-3 逆序的三位數 (10 分)

程序每次讀入一個正3位數,然后輸出按位逆序的數字。注意:當輸入的數字含有結尾的0時,輸出不應帶有前導的0。比如輸入700,輸出應該是7

#include<stdio.h>

void neg(int num);//逆序輸出

int main() {
	int num;
	scanf("%d", &num); //對於&num,num是數值,&num是地址
	neg(num);
	return 0;
}

void neg(int num) {
	int f = num % 10; //取個位
	int s = (num % 100 - f) / 10; //取十位,除10,保證轉移到個位
	int t = num / 100;//取百位
	if (f == 0 && s == 0) printf("%d", t);
	else if (f == 0) printf("%d%d", s, t);
	else printf("%d%d%d", f, s, t);//並列輸出
}

思路:用取余、除法取個、十、百位並單獨討論十位、百位都為0;百位為0的情況。並列輸出是解決這類題的好方法



7-4 BCD解密 (10 分)

BCD數是用一個字節來表達兩位十進制的數,每四個比特表示一位。所以如果一個BCD數的十六進制是0x12,它表達的就是十進制的12。但是小明沒學過BCD,把所有的BCD數都當作二進制數轉換成十進制輸出了。於是BCD0x12被輸出成了十進制的18了!

現在,你的程序要讀入這個錯誤的十進制數,然后輸出正確的十進制數。提示:你可以把18轉換回0x12,然后再轉換回12

#include<stdio.h>

void b2t(int num); //二進制轉十進制

int main() {
	int num;
	scanf("%d", &num); 
	b2t(num);
	return 0;
}

void b2t(int num) {
	int f = num & 0xf; //取個位
	int s = num >> 4; // 取十位
	if (s == 0) printf("%d", f); //單獨處理十位為0的情況
	else printf("%d%d", s, f);//並列輸出
}

思路:用位級運算取個、十位,再單獨處理十位為0的情況(因為最終是並列輸出,防止輸出兩個0)。位級運算有時確實比數級運算方便。



7-5 表格輸出 (5 分)

本題要求編寫程序,按照規定格式輸出表格。

#include<stdio.h>

int main() {
	printf("------------------------------------\n");
	printf("Province      Area(km2)   Pop.(10K)\n");
	printf("------------------------------------\n");
	printf("Anhui         139600.00   6461.00\n");
	printf("Beijing        16410.54   1180.70\n");
	printf("Chongqing      82400.00   3144.23\n");
	printf("Shanghai        6340.50   1360.26\n");
	printf("Zhejiang      101800.00   4894.00\n");
	printf("------------------------------------");
	return 0;
}

思路:注意最后一行無需用\n換行



7-6 混合類型數據格式化輸入 (5 分)

本題要求編寫程序,順序讀入浮點數1、整數、字符、浮點數2,再按照字符、整數、浮點數1、浮點數2的順序輸出。

#include<stdio.h>

int main() {
	int i;
	float f1, f2;
	char c;
	scanf("%f %d %c %f", &f1, &i, &c, &f2); //一開始寫成了%0.2f,但scanf()不支持帶點的格式寫法
	printf("%c %d %0.2f %0.2f", c, i, f1, f2);
	return 0;
}

//scanf()不支持帶點的格式寫法,可以有長度限制,但不可以有小數位數限制
//scanf(“%5f”,&f) 這樣寫可以!
//scanf(“%5.2f”,&f) 這樣寫不可以!
//scanf(“%0.2f”,&f) 這樣寫不可以!

思路:scanf()不支持帶點的格式寫法,可以有長度限制,但不可以有小數位數限制。



7-7 12-24小時制 (15 分)

編寫一個程序,要求用戶輸入24小時制的時間,然后顯示12小時制的時間。

#include<stdio.h>

int main() {
	int h, min;
	char c;
	scanf("%d%c%d", &h ,&c , &min);
	if (24 == h) printf("0%c%d AM", c, min); //用if判斷相等,常量在前,變量在后,可以避免將==寫成=
	else if (h > 12) printf("%d%c%d PM", h - 12, c , min);
	else if (12 == h) printf("%d%c%d PM", h, c , min);
	else printf("%d%c%d AM", h, c , min);
    return 0;
}

思路:注意題目並不要求將0輸出成00(其實我覺得這樣更好看)



7-8 超速判斷 (10 分)

模擬交通警察的雷達測速儀。輸入汽車速度,如果速度超出60 mph,則顯示“Speeding”,否則顯示“OK”。

#include<stdio.h>

int main() {
	int v;
	scanf("%d", &v);
	if (v > 60 ) printf("Speed: %d - speeding", v); //題目是超出60,暗示着不包括60
	else printf("Speed: %d - OK", v);
	return 0;
}

思路:題目是超出60,暗示着不包括60



7-9 用天平找小球 (10 分)

三個球A、B、C,大小形狀相同且其中有一個球與其他球重量不同。要求找出這個不一樣的球。

#include<stdio.h>

int main() {
	int A, B, C;
	scanf("%d %d %d", &A, &B, &C);
	if (A == B) printf("%c", 'C'); //look at the ansewer need the output is char but not numbers
	if (A == C) printf("%c", 'B');
	if (B == C) printf("%c", 'A');
	return 0;
}

思路:the ansewer need the output is char but not int.



7-10 計算工資 (15 分)

某公司員工的工資計算方法如下:一周內工作時間不超過40小時,按正常工作時間計酬;超出40小時的工作時間部分,按正常工作時間報酬的1.5倍計酬。員工按進公司時間分為新職工和老職工,進公司不少於5年的員工為老職工,5年以下的為新職工。新職工的正常工資為30元/小時,老職工的正常工資為50元/小時。請按該計酬方式計算員工的工資。

#include<stdio.h>

int main() {
	int p, h;
	scanf("%d %d", &p ,&h);
	if (p >= 5) {
		if (h > 40) printf("%0.2f", 40 * 50 + 1.5 * (h - 40) * 50);
		else printf("%0.2f", h * 50.0);
	}
	else {
		if (h > 40) printf("%0.2f", 40 * 30 + 1.5 * (h - 40) * 30);
		else printf("%0.2f", h * 30.0); //int * int also int, therefore, set to int * float
	}
    return 0;
}

思路:int * int also int, therefore, set to int * float in order to make flaot.



7-11 分段計算居民水費 (10 分)

為鼓勵居民節約用水,自來水公司采取按用水量階梯式計價的辦法,居民應交水費y(元)與月用水量x(噸)相關:當x不超過15噸時,y=4x/3;超過后,y=2.5x−17.5。請編寫程序實現水費的計算。

#include<stdio.h>

int main() {
	float x;
	scanf("%f", &x);
	if (x <= 15) printf("%0.2f", 4 * x / 3);
	if (x > 15) printf("%0.2f", 2.5 * x - 17.5);
	return 0;
}

思路:the input is float.



7-12 兩個數的簡單計算器 (10 分)

本題要求編寫一個簡單計算器程序,可根據輸入的運算符,對2個整數進行加、減、乘、除或求余運算。題目保證輸入和輸出均不超過整型范圍。

#include<stdio.h>

int main() {
	int a, b;
	char c;
	scanf("%d %c %d", &a, &c, &b);
	if(c == '+')
		printf("%d", a + b);
	else if(c == '-')
		printf("%d", a - b);
	else if(c == '*')
		printf("%d", a * b);
	else if(c == '/')
		printf("%d", a / b);
	else if(c == '%')
		printf("%d", a % b);
	else
		printf("ERROR");														
	return 0;
}

思路:easy.



7-13 日K蠟燭圖 (15 分)

股票價格漲跌趨勢,常用蠟燭圖技術中的K線圖來表示,分為按日的日K線、按周的周K線、按月的月K線等。以日K線為例,每天股票價格從開盤到收盤走完一天,對應一根蠟燭小圖,要表示四個價格:開盤價格Open(早上剛剛開始開盤買賣成交的第1筆價格)、收盤價格Close(下午收盤時最后一筆成交的價格)、中間的最高價High和最低價Low。

如果Close<Open,表示為“BW-Solid”(即“實心藍白蠟燭”);如果Close>Open,表示為“R-Hollow”(即“空心紅蠟燭”);如果Open等於Close,則為“R-Cross”(即“十字紅蠟燭”)。如果Low比Open和Close低,稱為“Lower Shadow”(即“有下影線”),如果High比Open和Close高,稱為“Upper Shadow”(即“有上影線”)。請編程序,根據給定的四個價格組合,判斷當日的蠟燭是一根什么樣的蠟燭。

#include<stdio.h>

int main() {
	int Open, High, Low, Close;
	scanf("%f %f %f %f", &Open ,&High, &Low, &Close);
	if (Close < Open) printf("BW-Solid");
	else if (Close > Open) printf("R-Hollow");
	else printf("R-Cross");
	if (Low < Open && Low < Close && High > Open && High > Close) //attention to order about "if", two&& big that one&&
		printf(" with Lower Shadow and Upper Shadow");
	else if (Low < Open && Low < Close) printf(" with Lower Shadow");
	else if (High > Open && High > Close) printf(" with Upper Shadow");
        return 0;
}

思路:compare quantity of condition, lots quantity at first and low quantity at second and so on.



7-14 求整數段和 (15 分)

給定兩個整數A和B,輸出從A到B的所有整數以及這些數的和。

//C(GUN) pass and C(Clang) pass 
#include<stdio.h>

int main() {
	int A, B;
	int i,j;
	int sum = 0;
	scanf("%d %d", &A, &B);
	if ((B - A) <= 4){ //if B - A <= 4, not have a newline.
		for(i = A; i <= B; i++){
			printf("%5d", i);
			sum += i;
	        }
	}
	else
	    for(i = A, j = 1; i <= B; i++, j++){
			printf("%5d", i);
			if ((j % 5) == 0) printf("\n");
			sum += i;
            }
	printf("\nSum = %d", sum);
	return 0;
}
//C(GUN) not pass but C(Clang) pass 
#include<stdio.h>

int main() {
	int A, B;
	int i,j;
	int sum;
	scanf("%d %d", &A, &B);
	for(i = A, j = 1; i <= B; i++, j++){
	printf("%5d", i);
    	if (B - A > 4)
		if ((j % 5) == 0) printf("\n"); //if B - A > 4, have a newline.
	sum += i;
	}
	printf("\nSum = %d", sum);
	return 0;
}

思路:if B - A <= 4, not have a newline.



7-15 計算圓周率 (15 分)

根據下面關系式,求圓周率的值,直到最后一項的值小於給定閾值。
π / 2 = 1 + 1/3 + 2!/3*5 + 3!/3*5*7 + ... + n!/3*5*7*...*(2n+1)

#include<stdio.h>

int main() {
	double PI = 1, up = 1, down = 1, last = 1;
	double i;
	double t;
	scanf("%lf", &t); //"scanf" function need address(&)
	for (i = 1; last >= t; i++){
                up *= i;
		down *= i * 2 + 1;
		last = up / down;
		PI += last;
	}
	printf("%0.6lf",  2 * PI);
	return 0;
}

思路:Attention to 0! equal 1!, so set PI = 1(First iteam) and set last = up / down = 1 / 3(Second iteam) at beginning.



7-16 求符合給定條件的整數集 (15 分)

給定不超過6的正整數A,考慮從A開始的連續4個數字。請輸出所有由它們組成的無重復數字的3位數。

//法一:用循環
#include <stdio.h>
int main(){
	int a, i, j, k, cnt = 0;
	scanf("%d", &a);
	for(i = a; i <= a+3; i++){
		for(j = a; j <= a+3; j++){
			if (i == j) continue;//若 (i == j)再一次執行第二個for循環
			for(k = a; k <= a+3; k++){
				if (i == k || j == k) continue;//若 (i == k || j == k) 再一次執行第三個for循環
				cnt++;
				if ((cnt % 6) == 0)	//保證每行末尾無空格
					printf("%d%d%d", i, j, k);
				else
					printf("%d%d%d ", i, j, k);
				if ((cnt % 6) == 0 && cnt <= 18) printf("\n"); //最后一次不換行
			}
		}
	}
	return 0;
}

思路:i,j,k按遞增順序分別對應四個數,第二個for保證j != i,第三個for保證k != i && k != j
它們都是按遞增順序比較是否相等之后再輸出,保證了輸出是遞增的。
在輸出的每一行中,第一個for調用一次,第二個for將調用四次,第三個for將調用十八次。

//法二:暴力枚舉
#include<stdio.h>

int main() {
	int a;
	scanf("%d", &a);
	int b = a + 1;
	int c = a + 2;
	int d = a + 3; 
	printf("%d %d %d %d %d %d\n", a*100+b*10+c, a*100+b*10+d, a*100+c*10+b, a*100+c*10+d, a*100+d*10+b, a*100+d*10+c);
	printf("%d %d %d %d %d %d\n", b*100+a*10+c, b*100+a*10+d, b*100+c*10+a, b*100+c*10+d, b*100+d*10+a, b*100+d*10+c);
	printf("%d %d %d %d %d %d\n", c*100+a*10+b, c*100+a*10+d, c*100+b*10+a, c*100+b*10+d, c*100+d*10+a, c*100+d*10+b);
	printf("%d %d %d %d %d %d\n", d*100+a*10+b, d*100+a*10+c, d*100+b*10+a, d*100+b*10+c, d*100+c*10+a, d*100+c*10+b);
}




7-17 爬動的蠕蟲 (15 分)

一條蠕蟲長1寸,在一口深為N寸的井的底部。已知蠕蟲每1分鍾可以向上爬U寸,但必須休息1分鍾才能接着往上爬。在休息的過程中,蠕蟲又下滑了D寸。就這樣,上爬和下滑重復進行。請問,蠕蟲需要多長時間才能爬出井?

這里要求不足1分鍾按1分鍾計,並且假定只要在某次上爬過程中蠕蟲的頭部到達了井的頂部,那么蠕蟲就完成任務了。初始時,蠕蟲是趴在井底的(即高度為0)。

#include<stdio.h>

int main(){
  int n,u,d;
  scanf("%d%d%d",&n,&u,&d);
  int t = 0,place = 0;//時間、位置初始化為0
  while(1){
    place += u; //向上爬
	t++; //增加向上爬的時間
    if(place >= n){ //如果身體爬出井或剛好頭部接觸井沿,則結束循環
      printf("%d", t);
      break;
    }
    place -= d; //向下滑
	t++;//增加向下滑的時間
  }
	return 0;
}

思路:完全依照題意進行編程,重點是運用了增量這個概念,對時間進行遞增,對位置進行遞增和遞減。



7-18 二分法求多項式單根 (20 分)

二分法求函數根的原理為:如果連續函數f(x)在區間[a,b]的兩個端點取值異號,即f(a)f(b)<0,則它在這個區間內至少存在1個根r,即f(r)=0。

#include <stdio.h>
//f(x) = a3*x^3 + a2*x^2 +  a1*x + a0
double func(double a3, double a2, double a1, double a0, double x){ 
	return a3 *x*x*x + a2 *x*x + a1 *x +a0; 
}

int main() {
	double a3, a2, a1, a0, a, b, mid, func_a, func_b, func_mid;
	scanf("%lf %lf %lf %lf %lf %lf", &a3, &a2, &a1, &a0, &a, &b); //why forget `&` again ?
	while(b - a > 0.001){
		mid = (a + b) / 2;
		func_a = func(a3, a2, a1, a0, a);
		func_b = func(a3, a2, a1, a0, b);
		func_mid = func(a3, a2, a1, a0, mid);
		if (func_a * func_b < 0){
			if(func_mid == 0) {
				printf("%.2f", mid); //find it have lots time, mid != func_mid
				return 0;
			}
			else if(func_a * func_mid > 0)
      //euqal (func_mid>0 && func_a>0) || (func_mid<0 && func_a<0)
				a = mid;
			else if(func_b * func_mid > 0)
      //euqal (func_mid>0 && func_b>0) || (func_mid<0 && func_b<0)
				b = mid; //x and y have the same sign equal x*y > 0
		}
		else if(func_a * func_b == 0){
			if(func_a == 0){
				printf("%.2f", a);
				return 0;
			}
			else if(func_b == 0){
				printf("%.2f", b);
				return 0;
			}
		}
	}
	printf("%.2f", (a + b) / 2);
	return 0;
}
//segmentation fault
int* get(){
	int *coef;
	int i;
	for(i = 0; i <= 3; i++)
		scanf("%d", &coef[i]); //coef[0] ~ coef[3] order to a3 ~ a0
	return coef;
}
// can't use array[], because it will segmentation fault

int *coef = get();

double func(int *coef, double x){ //f(x) = a3*x^3 + a2*x^2 +  a1*x + a0
	int i; 
	return coef[0] *x*x*x + coef[1] *x*x +coef[2] *x +coef[3]; 
  // can't use pow() function, because it will segmentation fault
}

thoughts:

  • try to submit array address to func, but segmentation fault.
  • try to use double pow(double x , double n) , but also segmentation fault.
  • x and y have the same sign equal x*y > 0.
  • forget & again...when I no pay attention to it.
  • mid != func_mid! ! ! !



7-19 支票面額 (15 分)

一個采購員去銀行兌換一張y元f分的支票,結果出納員錯給了f元y分。采購員用去了n分之后才發覺有錯,於是清點了余額尚有2y元2f分,問該支票面額是多少?

#include<stdio.h>

int main(){
  int y, f, n;
  int i = 0; //支票面額從0開始
  scanf("%d", &n);
  while(i <= 10000){ //題目應給出支票面額的取值范圍,否則會超時
    y = i / 100;
    f = i % 100;
    if (f*100 + y - n == 2*y*100 + 2*f){
      printf("%d.%d", y, f);
      return 0; //跳出循環,並且結束函數
    }
    i++;
  }
  printf("No Solution");
  return 0;
}

思路:完全依照題意進行編程,思路上按照題意來,然后逐個解決。我是將輸出結果看成四位數,然后分別取前兩位,后兩位。f*100 + y - n == 2*y*100 + 2*f其實是對“結果出納員錯給了f元y分。采購員用去了n分之后才發覺有錯,於是清點了余額尚有2y元2f分”的數學表示。注意,return 0可以不止寫一條,只要滿足要求



7-20 打印九九口訣表 (15 分)

下面是一個完整的的下三角九九口訣表:

1*1=1   
1*2=2   2*2=4   
1*3=3   2*3=6   3*3=9   
1*4=4   2*4=8   3*4=12  4*4=16  
1*5=5   2*5=10  3*5=15  4*5=20  5*5=25  
1*6=6   2*6=12  3*6=18  4*6=24  5*6=30  6*6=36  
1*7=7   2*7=14  3*7=21  4*7=28  5*7=35  6*7=42  7*7=49  
1*8=8   2*8=16  3*8=24  4*8=32  5*8=40  6*8=48  7*8=56  8*8=64  
1*9=9   2*9=18  3*9=27  4*9=36  5*9=45  6*9=54  7*9=63  8*9=72  9*9=81  

本題要求對任意給定的一位正整數N,輸出從1*1N*N的部分口訣表。

#include<stdio.h>

int main(){
  int i, j, n;
  scanf("%d",&n);
  for(i = 1; i <= n; i++)
    for(j = 1; j <= i; j++){
      if (j*i <= 9 ) printf("%d*%d=%d   ", j, i, j*i);
      else printf("%d*%d=%d  ", j, i, j*i);
      if(j == i) printf("\n");
  }
	return 0;
}

思路:注意輸出格式,左對齊,等式右端包括數值一共占4



7-21 求特殊方程的正整數解 (15 分)

本題要求對任意給定的正整數N,求方程X2+*Y*2=N的全部正整數解。

//個人解法一
#include <stdio.h>

int main() {
	int i, j, n, flag = 0, a[10], b[10];
	scanf("%d", &n); //scanf的輸入是存在地址上的
	for(i = 1; i <= 100; i++)
		for(j = 1; j <= i; j++){
			if (i*i + j*j == n) {
				a[flag] = j;
				b[flag] = i;
				flag++;
			}
			// else 
			// 	continue;
		}
	if(!flag) 
		printf("No Solution");
	else
		for(i = flag - 1; i >= 0; i--)
			printf("%d %d\n", a[i], b[i]);
	return 0;
}
//參考解法二
#include<stdio.h>

int main() {
  int n;
  scanf("%d", &n);
  int i, j, flag=1;
  for(i=1; i<=100; i++)
    for(j=1; j<=100; j++)
      if(i*i + j*j == n && i < j){
        printf("%d %d\n",i,j);
        flag=0;
      }
  if(flag) printf("No Solution");
  return 0;
}

思路:個人解法中,使用數組使得x<=y,但過於繁瑣了,不如參考解法中的i,j共同遍歷,滿足i < j即可。同時需要注意到題目要求是正整數求解,也就是說不包括0和負數。



7-22 龜兔賽跑 (20 分)

烏龜與兔子進行賽跑,跑場是一個矩型跑道,跑道邊可以隨地進行休息。烏龜每分鍾可以前進3米,兔子每分鍾前進9米;兔子嫌烏龜跑得慢,覺得肯定能跑贏烏龜,於是,每跑10分鍾回頭看一下烏龜,若發現自己超過烏龜,就在路邊休息,每次休息30分鍾,否則繼續跑10分鍾;而烏龜非常努力,一直跑,不休息。假定烏龜與兔子在同一起點同一時刻開始起跑,請問T分鍾后烏龜和兔子誰跑得快?

#include<stdio.h>

int main(){
	int rab_speed = 9, tur_speed = 3;
	int rabbit = 0, turtle = 0;
	int T, rab_sleep = -1, rab_time = 10; 
  /*rab_sleep = -1 because line 17 of 'rab_sleep != 0' and line 22 of 'rab_sleep-- == 0'
  rabbit can't sleep at bgain and We can't every minutes reset 'rab_time = 10'
  */
	scanf("%d", &T);
	while(T--){// count down T
		turtle += tur_speed;
		if(rab_time-- > 0)// count down 10_min
			rabbit += rab_speed;
		if(rab_time == 0){ // Execute when the 10 min countdown is over
		/* rabbit above turtle and rabbit not sleep */
			if(rabbit > turtle && rab_sleep != 0) 
			rab_sleep = 30;
			else 
			rab_time = 10;
    }
		if(rab_sleep-- == 0)// count down sleep_time
			rab_time = 10;
	}
	if(rabbit > turtle)
		printf("^_^ %d", rabbit);
	else if(rabbit < turtle)
		printf("@_@ %d", turtle);
	else
		printf("-_- %d", rabbit);
	return 0;
}

thought:learn using countdown design.



7-23 幣值轉換 (20 分)

輸入一個整數(位數不超過9位)代表一個人民幣值(單位為元),請轉換成財務要求的大寫中文格式。如23108元,轉換后變成“貳萬叄仟壹百零捌”元。為了簡化輸出,用小寫英文字母a-j順序代表大寫數字0-9,用S、B、Q、W、Y分別代表拾、百、仟、萬、億。於是23108元應被轉換輸出為“cWdQbBai”元。

#include<stdio.h>
int main()
{
	int num, temp;
	int count = 0;
	int dec; 
	int i, j;
	int flag = 0;
	int data[10];
	scanf("%d",&num);
	temp = num;
	if(num == 0){//單個0時輸出
		printf("a");
	}
	while(num != 0){//將數字分解,並存入數組data[]中
		i = num % 10; //取最低一位
		data[count] = i;
		count++;
		num = num / 10; //截掉最低位,更新num
	}
	dec = count + 1; //之所以要+1是因為每次使用前要自減(line23)
	for(j = count-1; j >= 0; j--){//j = count-1,是因為while循環結束時,count還要自加一次,減去1以抵消這一次自加
		dec--;// dec表示數字分解后,總共分解出多少個數字
		if(temp > 10000 && data[j] == 0 && data[j-1] == 0 && data[1] != 0 && flag == 0){
			//超過一萬,中間連續多個0解決方法
			//原理是要滿足:num大於一萬,而且至少有兩個連續的0,以及num的從右往左數,第2個數為不為0,即data[1] != 0.
			flag++; //最后要用flag保證滿足時只輸出一次'W'。
			printf("W");
		}
		if(data[j] == 0 && data[j-1] == 0){//只滿足至少有兩個連續的0,則重新開始循環
			continue;
		}
		switch(data[j]){//數字轉字母
			case 0:
				printf("a");
				break;
			case 1:
				printf("b");
				break;
			case 2:
				printf("c");
				break;
			case 3:
				printf("d");
				break;
			case 4:
				printf("e");
				break;
			case 5:
				printf("f");
				break;
			case 6:
				printf("g");
				break;
			case 7:
				printf("h");
				break;
			case 8:
				printf("i");
				break;
			case 9:
				printf("j");
				break;
		}	
		if(data[j] != 0){//數字轉單位
			switch(dec)//直到用完分解的數才結束
			{
				case 1:
					break;
				case 2:
					printf("S");
					break;
				case 3:
					printf("B");
					break;
				case 4:
					printf("Q");
					break;
				case 5:
					printf("W");
					break;
				case 6:
					printf("S");
					break;
				case 7:
					printf("B");
					break;
				case 8:
					printf("Q");
					break;
				case 9:
					printf("Y");
					break;
			}	
		}
	}
	return 0;
}

思路:從最低位分解數字,之后要考慮中間連續為0時,是否有過萬。



7-24 約分最簡分式 (15 分)

分數可以表示為分子/分母的形式。編寫一個程序,要求用戶輸入一個分數,然后將其約分為最簡分式。最簡分式是指分子和分母不具有可以約分的成分了。如6/12可以被約分為1/2。當分子大於分母時,不需要表達為整數又分數的形式,即11/8還是11/8;而當分子分母相等時,仍然表達為1/1的分數形式。

#include <stdio.h>

int main(){
  int num, den, x, x_max, com_fac_max;
  char sem;
  scanf("%d%c%d", &num, &sem, &den);
  x_max = num < den ? num : den; //smaller number as x_max
  for(x = 1; x <= x_max; x++)//find 'com_fac_max'
    if (num % x == 0 && den % x == 0) 
      com_fac_max = x; //updata 'com_fac_max'
  printf("%d%c%d", num / com_fac_max, sem, den / com_fac_max);
  return 0;
}

though: traveral 1 ~ x_max to find com_fac_max(maxinum of common factor).

還找到了一個簡潔優雅的遞歸函數來求最大公因數:

#include <stdio.h>

int gcd(int x, int y){return y ? gcd(y, x%y) : x;}//find maxinum of common factor

int main(){
  int num, den, com_fac_max;
  char sem;
  scanf("%d%c%d", &num, &sem, &den);
  com_fac_max = gcd(num, den);
  printf("%d%c%d", num / com_fac_max, sem, den / com_fac_max);
  return 0;
}




7-25 念數字 (15 分)

輸入一個整數,輸出每個數字對應的拼音。當整數為負數時,先輸出fu字。十個數字對應的拼音如下:

0: ling
1: yi
2: er
3: san
4: si
5: wu
6: liu
7: qi
8: ba
9: jiu

AC代碼:

#include <stdio.h>
int main(){
  int num, cut[100], i;
  scanf("%d", &num);
  if (num == 0){ 
    printf("ling"); 
    return 0;
  }
  if (num < 0){
    printf("fu ");
    num = ~num + 1;//取補碼,即取絕對值,但對int_min = -2147483648無效
  }
  if (num == -2147483648){//單獨拎出來輸出
    printf("fu er yi si qi si ba san liu si ba");
    return 0;
  }
  for(i = 1; num != 0 ;i++){ //當不等於0時才執行
    cut[i] = num % 10;
    num /= 10; //更新num
  }
  i--;
  while(i){
    switch (cut [i]){
    case 0:
      printf("ling");
      break;
    case 1:
      printf("yi");
      break;
    case 2:
      printf("er");
      break;
    case 3:
      printf("san");
      break;
    case 4:
      printf("si");
      break;
    case 5:
      printf("wu");
      break;
    case 6:
      printf("liu");
      break;
    case 7:
      printf("qi");
      break;
    case 8:
      printf("ba");
      break;
    case 9:
      printf("jiu");
      break;      
    }
    i--; //降序輸出
    if (i != 0) //確保最后一個數字之后沒有空格,而其它數字之后有空格
      printf(" ");
  }  
  return 0;
}

思路:不斷分割輸入的整數,但注意for循環結束后的i,比所需的cut[i]的i值多了1,需要減去。同時輸出時是按i值降序(從大到小)輸出的。



7-26 單詞長度 (15 分)

你的程序要讀入一行文本,其中以空格分隔為若干個單詞,以.結束。你要輸出每個單詞的長度。這里的單詞與語言無關,可以包括各種符號,比如it's算一個單詞,長度為4。注意,行中可能出現連續的空格;最后的.不計算在內。

#include <stdio.h>

int main (){
  char c;
  int flag = 0, count;
  while(1){
    count = 0; //set(reset) count
    scanf("%c",&c); //set(reset) first character
    while(c != ' ' && c != '.'){ //count and input
      count++; 
      scanf("%c", &c);
    }
    if(count != 0){
      flag++;
      if(flag == 1) printf("%d", count); //first number
      else printf(" %d", count); //other numbers
    }
    if(c == '.') break;
  }
  return 0;
}

ideas:

  • Before counting, we must set(reset) count and the first c.
  • The format of output are one numbers: %d or some numbers: %d %d %d %d, so we must set flag.




7-27 冒泡法排序 (20 分)

N個整數按從小到大排序的冒泡排序法是這樣工作的:從頭到尾比較相鄰兩個元素,如果前面的元素大於其緊隨的后面元素,則交換它們。通過一遍掃描,則最后一個元素必定是最大的元素。然后用同樣的方法對前N−1個元素進行第二遍掃描。依此類推,最后只需處理兩個元素,就完成了對N個數的排序。

本題要求對任意給定的K(<N),輸出掃描完第K遍后的中間結果數列。

輸入格式:

輸入在第1行中給出NK(1≤K<N≤100),在第2行中給出N個待排序的整數,數字間以空格分隔。

輸出格式:

在一行中輸出冒泡排序法掃描完第K遍后的中間結果數列,數字間以空格分隔,但末尾不得有多余空格。

輸入樣例:

6 2
2 3 5 1 6 4
結尾無空行

輸出樣例:

2 1 3 4 5 6
結尾無空行

AC code:

#include <stdio.h>

int main(){
  int N, K;
  int i, j;
  scanf("%d %d", &N, &K);
  int num[N];
  int temp, flag;
  for(i = 0; i < N; i++){
    scanf("%d", &num[i]);
  }
  for(i = 0; i < K; i++){
    for(j = 1; j < N; j++){
      if(num[j - 1] > num[j]){ //swap
        temp = num[j];
        num[j] = num[j - 1];
        num[j - 1] = temp;
      }
    }
    N--; //last number already is max, so reset `N`
  }
  for(i = 0; i < N+K; i++){ //use flag in order to Format
    if(flag == 0)
      printf("%d", num[i]);
    else
      printf(" %d", num[i]);
    flag++;
  }
  return 0;
}

ideas:

  • Begain using debug;
  • contiune running of models;
  • step over running of ''one by one''("line by line");
  • step into into this function, and then do this function of ''one by one'';
  • step out leave this function.




7-28 猴子選大王 (20 分)

一群猴子要選新猴王。新猴王的選擇方法是:讓N只候選猴子圍成一圈,從某位置起順序編號為1~N號。從第1號開始報數,每輪從1報到3,凡報到3的猴子即退出圈子,接着又從緊鄰的下一只猴子開始同樣的報數。如此不斷循環,最后剩下的一只猴子就選為猴王。請問是原來第幾號猴子當選猴王?

AC code:

#include <stdio.h>

int main(){
	int N;
	scanf("%d", &N);
	int a[N];
	int i;
	for (i=0; i<N; i++)
		a[i]=i+1;
	int elimination = 0; //淘汰變量 
	int remainder = N; //剩余變量 
	while (remainder > 1){
		for (i=0; i<N; i++){
			if (a[i] == 0) 
				continue; 
			//序號為0的猴已淘汰,不計數,直接進入下一輪循環 
			elimination++;
			if (elimination == 3){
				a[i] = 0; //淘汰之后的猴子序號設為0 
				elimination = 0; //淘汰變量重置 
				remainder--; //剩余猴數減1 
			}
		}
	}
	for (i=0; i<N; i++)
		if (a[i] != 0)
			printf("%d\n", i+1);
	return 0;
}

思路:引入兩個計數變量,第一個變量作為淘汰變量,每數到3淘汰一個,重新計數;
第二個變量作為剩余變量,記錄剩余猴子數,每淘汰一個減少1,當剩余變量為1時,結束。




7-29 刪除字符串中的子串 (20 分)

輸入2個字符串S1和S2,要求刪除字符串S1中出現的所有子串S2,即結果字符串中不能包含S2。

AC code:

#include<stdio.h>
#include<string.h>

int main (){
	char s1[100], s2[100], temp[100];
	gets(s1);
	gets(s2);
	char *s1_s2; //當s1中含有s2時, s1_s2視為s1中的s2首地址
	while(s1_s2 = strstr(s1, s2)){ //比對,當s1中含有s2時,執行循環(此時s1_s2為s1中的s2首地址);若s1中不含有s2,則循環結束(此時s1_s2為NULL)
		strcpy(temp, s1_s2 + strlen(s2)); //s1_s2加上s2的長度用來跳過想要刪除的子串,將跳過之后的字符串復制到臨時數組 
		*s1_s2 = '\0'; //把s1_s2此時存放的字符變為'\0',以下一步用strcat實現拼接
		strcat(s1, temp); //將臨時數組拼接在s1_s2的位置之后(即s1的第一個終止符'\0'之后) 
	}
	puts(s1);
	return 0;
}

解答二:利用string.h庫中的字符串函數解決

提示:在 C 語言中,字符串實際上是使用字符 \0 終止的一維字符數組。

先來介紹要用到的字符串函數:

1、char *strstr( const char *str1, const char *str2 );

功能:用於判斷字符串str2是否是str1的子串。如果是,則該函數返回 str1字符串從 str2第一次出現的位置開始到 str1結尾的字符串;否則,返回NULL。

2、char *strcpy( char *str1, const char *str2 );

功能:復制,把從str2地址開始且含有NULL結束符的字符串復制到以str1開始的地址空間

3、char *strcat( char *str1, const char *str2 );

功能:拼接,把str2所指向的字符串(包括\0)復制到str1所指向的字符串后面(刪除str1原來末尾的\0)。注意要保證str1足夠長,以容納被復制進來的*str2。

注意:以上兩個函數(strcpy,strcat)中str1和str2所指內存區域不可以重疊且str1必須有足夠的空間來容納str2的字符串。因此代碼中定義了一個臨時數組。

解法參考鏈接字符串函數參考鏈接c語言NULL和0區別及NULL詳解




7-30 字符串的冒泡排序 (20 分)

我們已經知道了將N個整數按從小到大排序的冒泡排序法(7-27)。本題要求將此方法用於字符串序列,並對任意給定的K(<N),輸出掃描完第K遍后的中間結果序列。

AC code:

#include <stdio.h>
#include <string.h>

int main(){
  int N, K;
  int i, j;
  scanf("%d %d", &N, &K);
  char str[N][11];//using two-dimensional array so that each element is a string
  char temp[11]; //if have 10 character, the 11 character is '\0'
  for(i = 0; i < N; i++){
    scanf("%s", &str[i]);
  }
  for(i = 0; i < K; i++){
    for(j = 0; j < N - 1; j++){
      if(strcmp(str[j], str[j+1]) > 0){ //swap
				strcpy(temp, str[j]);
				strcpy(str[j], str[j+1]);
				strcpy(str[j+1], temp);
      }
    }
    N--; //last strber already is max, so reset `N`
  }
	for(i = 0; i < N+K; i++)
		printf("%s\n", str[i]);
	return 0;
}

提示:在 C 語言中,字符串實際上是使用字符 \0 終止的一維字符數組。

介紹string.h中的字符串函數:

  • char *strcpy( char *str1, const char *str2 );
    功能:復制,把從str2地址開始且含有NULL結束符的字符串復制到以str1開始的地址空間
  • int *strcmp( char *str1, const char *str2 );
    用於比較兩個字符串並根據比較結果返回整數。若str1=str2,則返回零;若str1<str2,則返回負數;若str1>str2,則返回正數。

字符串函數參考鏈接c語言NULL和0區別及NULL詳解




7-31 字符串循環左移 (20 分)

輸入一個字符串和一個非負整數N,要求將字符串循環左移N次。

輸入格式:

輸入在第1行中給出一個不超過100個字符長度的、以回車結束的非空字符串;第2行給出非負整數N

輸出格式:

在一行中輸出循環左移N次后的字符串。

輸入樣例:

Hello World!
2
結尾無空行

輸出樣例:

llo World!He
結尾無空行

AC code:

#include <stdio.h>
#include <string.h>

int main(){
  int n;
  char s1[101], s2[101], temp[101];
  char* s1_s2;
  gets(s1);
  scanf("%d", &n);
  int len = strlen(s1);
  if(n % len == 0){ //由於是循環左移,要考慮n是s1長度的倍數的情況
    puts(s1);
    return 0;
  }
  if(n > len){ //由於是循環左移,要考慮n大於s1長度的情況
    n -= (n / len) * len;
  }
  strncpy(s2, s1, n);
  s1_s2 = strstr(s1, s2);
  strcpy(temp, s1_s2 + n);
  *s1_s2 = '\0';
  strcat(s1, temp);
  strncat(s1, s2, n);
  // puts(s1);
  for(int i = 0; i < len; i++){
    printf("%c", s1[i]);
  }
  return 0;
}

思路:又學習了兩個字符串函數;參考了7-29,但是要區別這是循環左移;最后發現puts()函數會導致段錯誤

提示:在 C 語言中,字符串實際上是使用字符 \0 終止的一維字符數組。

介紹string.h中的字符串函數:

  • char *strncpy( char *str1, const char *str2, int n );
    功能:復制,把從str2地址開始且含有NULL結束符的前n個字符串復制到以str1開始的地址空間
  • char strncat( char *str1, const char *str2, int n );
    功能:拼接,把str2所指向的字符串的前n個字符(再加一個\0)復制到str1所指向的字符串后面(刪除str1原來末尾的\0)。注意要保證str1足夠長,以容納被復制進來的str2




7-32 說反話-加強版 (20 分)

給定一句英語,要求你編寫程序,將句中所有單詞的順序顛倒輸出。

輸入格式:

測試輸入包含一個測試用例,在一行內給出總長度不超過500 000的字符串。字符串由若干單詞和若干空格組成,其中單詞是由英文字母(大小寫有區分)組成的字符串,單詞之間用若干個空格分開。

輸出格式:

每個測試用例的輸出占一行,輸出倒序后的句子,並且保證單詞間只有1個空格。

輸入樣例:

Hello World   Here I Come
結尾無空行

輸出樣例:

Come I Here World Hello
結尾無空行

AC code:

#include<stdio.h>//標准c,沒有用c++的string,這樣首先讀取字符串就是個問題了 
#define MAX 500000


int main (){
    char c;//存放單獨一個字符 
    char t[MAX];//創建一個字符數組 
    int i = 0, count = 0, flag = 0;

    //先處理字符串,刪除多余的空格(包括頭尾、中間的空格),形成新字符串 
    while ((c = getchar()) != '\n') {//getchar每次從標准輸入讀入一個字符 ,標准輸入會有'\n'??? 
        if (c != ' ') {
            flag = 1; //標記遇到單詞 
            t[i++] = c;
            count = 0;
        } 
        else if (count > 0) {
            continue;//遇到連續兩個空格或以上用contiue跳過
        } 
        else if (flag) {
            t[i++] = c; //只有之前遇到單詞的情況下碰到空格才把這個空格寫入目標字符串 
            count = 1;
        }
    }
    //刪除多余的空格,將目標字符串放入 t 中 
    //這里的count起了什么作用呢? 
    //如遇到 Hello,都存入t中,遇到第一個空格,此時count=0,flag=1,把這個空格存入t,count=1
	//遇到連續兩個空格或以上用continue跳過作用,如World之后有三個空格
    //開頭就是空格咋辦?啥也不操作 
    
    //倒着輸出單詞
    count = 0;
    int j;
    for (i -= 1; i >= 0; i--) {//最后一個標號為i里面是存'\0'的,一開始要用i-=1避免輸出 
        if (t[i] != ' ') {
            count ++; // 這里的 count 統計的是一個單詞里字母的個數 
        } else if (t[i] == ' ' && count > 0) {//遇到空格就輸出單詞 
            for (j = i+1; j <= i+count; j++) {//但注意對於單個字母而言是順序輸出,對單個單詞是倒着輸出
                printf("%c", t[j]);
            } 
            printf(" ");
            count = 0;
        }
    }
    // 還剩最后一個單詞沒輸出,因為最后一個單詞前方無空格,只完成了count++,但是遇不到空格,那么邏輯是一樣的
    count--;
    for (j = 0; j <= count; j++) {//輸出最后一個單詞
        printf("%c", t[j]);
    } 
    return 0; 
}

思路:多使用標記,如flag, countReference Linking




7-33 有理數加法 (15 分)

本題要求編寫程序,計算兩個有理數的和。

輸入格式:

輸入在一行中按照a1/b1 a2/b2的格式給出兩個分數形式的有理數,其中分子和分母全是整形范圍內的正整數。

輸出格式:

在一行中按照a/b的格式輸出兩個有理數的和。注意必須是該有理數的最簡分數形式,若分母為1,則只輸出分子。

輸入樣例1:

1/3 1/6
結尾無空行

輸出樣例1:

1/2
結尾無空行

輸入樣例2:

4/3 2/3

輸出樣例2:

2

AC code:

#include <stdio.h>

int gcd(int x, int y){return y ? gcd(y, x%y) : x;}//find maxinum of common factor

int main(){
	int a1,a2,b1,b2;
	int a,b;
  
	scanf("%d/%d %d/%d", &a1, &b1, &a2, &b2);
	if(b1 == b2){ //add
		a = a1 + a2;
		b = b1;
	}
	else{
		b = b1 * b2;
		a1 = a1 * b2;
		a2 = a2 * b1;
		a = a1 + a2;
	}

	if(a%b == 0) //can exact division
	  printf("%d", a/b);
	else{ //find common factor of maximum
    int com_fac_max = gcd(a, b);
	  printf("%d/%d", a/com_fac_max, b/com_fac_max);
  }
	return 0;
}

idea: refer to [7-24 約分最簡分式 ](# 7-24 約分最簡分式 (15 分))
ps: Recently, I will spend a lot of time preparing for CET6.




7-34 通訊錄的錄入與顯示 (10 分)

通訊錄中的一條記錄包含下述基本信息:朋友的姓名、出生日期、性別、固定電話號碼、移動電話號碼。 本題要求編寫程序,錄入N條記錄,並且根據要求顯示任意某條記錄。

輸入格式:

輸入在第一行給出正整數N(≤10);隨后N行,每行按照格式姓名 生日 性別 固話 手機給出一條記錄。其中姓名是不超過10個字符、不包含空格的非空字符串;生日按yyyy/mm/dd的格式給出年月日;性別用M表示“男”、F表示“女”;固話手機均為不超過15位的連續數字,前面有可能出現+

在通訊錄記錄輸入完成后,最后一行給出正整數K,並且隨后給出K個整數,表示要查詢的記錄編號(從0到N−1順序編號)。數字間以空格分隔。

輸出格式:

對每一條要查詢的記錄編號,在一行中按照姓名 固話 手機 性別 生日的格式輸出該記錄。若要查詢的記錄不存在,則輸出Not Found

輸入樣例:

3
Chris 1984/03/10 F +86181779452 13707010007
LaoLao 1967/11/30 F 057187951100 +8618618623333
QiaoLin 1980/01/01 M 84172333 10086
2 1 7
結尾無空行

輸出樣例:

LaoLao 057187951100 +8618618623333 F 1967/11/30
Not Found
結尾無空行

AC code:

#include<stdio.h>

struct unit{
  char name[11]; //10 characters + 1 '\0' = 16 char
  char day[11];
  char gender;
  char telephone[17]; //15 numbers + 1 '\0' + 1 char of '+' = 17 char
  char mobile[17];
};

int main() {
  int N, K, num;
  int i;
  scanf("%d\n", &N);
  struct unit info[N];
  for(i = 0; i < N; i++){
    scanf("%s %s %c %s %s", &info[i].name, &info[i].day, &info[i].gender, &info[i].telephone, &info[i].mobile);
  }
  scanf("\n%d", &K); // After inputing all strings, you input K 
  for(i = 0; i < K; i++){
    scanf("%d", &num);
    if(num >=0 && num < N) //num is integer so maybe < 0
      printf("%s %s %s %c %s\n", info[num].name, info[num].telephone, info[num].mobile, info[num].gender, info[num].day);
    else
      printf("Not Found\n");
  }
  return 0;
}

idea: use struct array and string %s.




7-35 有理數均值 (20 分)

日期:21.11.8

本題要求編寫程序,計算N個有理數的平均值。

輸入格式:

輸入第一行給出正整數N(≤100);第二行中按照a1/b1 a2/b2 …的格式給出N個分數形式的有理數,其中分子和分母全是整形范圍內的整數;如果是負數,則負號一定出現在最前面。

輸出格式:

在一行中按照a/b的格式輸出N個有理數的平均值。注意必須是該有理數的最簡分數形式,若分母為1,則只輸出分子。

輸入樣例1:

4
1/2 1/6 3/6 -5/10
結尾無空行

輸出樣例1:

1/6
結尾無空行

輸入樣例2:

2
4/3 2/3

輸出樣例2:

1

AC code:

#include <stdio.h>

int gcd(int x, int y){return y ? gcd(y, x%y) : x;}//find maxinum of common factor

int main(){
	int sum = 0, N, com_fac_max;
	scanf("%d", &N);
	int a[N], b[N];
	for (int i = 0; i < N; i++){//每次輸入都約分防止求和時溢出
		scanf("%d/%d", &a[i], &b[i]);
		com_fac_max = gcd(a[i], b[i]);
		a[i] = a[i] / com_fac_max;
		b[i] = b[i] / com_fac_max;		
	}

	int big, min, last_min;
	if (N == 1){
		min = b[0];
		last_min = b[0];
	}
	else{ //求分母的最小公約數,按順序兩兩比較來求
		big = b[0] > b[1] ? b[0] : b[1]; //如果只有兩個數,要單獨找
		for (int i = 0; i < N - 1; i++){ //'i < N - 1'是因為最后一個分母是b[(N-2) + 1] = b[N-1]
			big = big > b[i+1] ? big : b[i+1]; //找大的分母
			for(int j = 1; j < 100; j++){ 
				min = big * j; //用大的分母不斷線性遞增
				if(min % b[i+1] == 0 && min % b[i] == 0){ //使得恰好兩個分母都能整除
					last_min = min; //更新分母的最小公約數
					break;
				}
			}
		}
	}
	
	for (int i = 0; i < N; i++){//統一分母為最小公約數后,化簡分子
			a[i] = last_min / b[i] * a[i];
	}

	for (int i = 0; i < N; i++){//分子求和
			sum += a[i];
	}

	int down = last_min * N; //分母記得乘上數據個數,以求均值

	if(sum % down == 0) //分子可以完全整除分母時,就直接除
	  printf("%d", sum/down);
	else{ //否則再約分一次
    com_fac_max = gcd(sum, down);
	  printf("%d/%d", sum / com_fac_max, down / com_fac_max);
  }		
	return 0;
}

思路:

  • 主要是求多個分母的最小公約數那一塊,要考慮到只有一個數、只有兩個數的邊界情況。本質上還是用for循環一個個找,使得能夠兩兩被整除。
  • 報錯提示:“若不隨時化簡,則會溢出”,告訴我們要考慮到分子求和可能會溢出的情況,所以每輸入一次就化簡一次。
  • gcd(int x, int y)仍舊源自 [7-24 約分最簡分式 ](# 7-24 約分最簡分式 (15 分))




7-36 復數四則運算 (15 分)

日期:21.11.22

本題要求編寫程序,計算2個復數的和、差、積、商。

輸入格式:

輸入在一行中按照a1 b1 a2 b2的格式給出2個復數C1=a1+b1i和C2=a2+b2i的實部和虛部。題目保證C2不為0。

輸出格式:

分別在4行中按照(a1+b1i) 運算符 (a2+b2i) = 結果的格式順序輸出2個復數的和、差、積、商,數字精確到小數點后1位。如果結果的實部或者虛部為0,則不輸出。如果結果為0,則輸出0.0。

輸入樣例1:

2 3.08 -2.04 5.06
結尾無空行

輸出樣例1:

(2.0+3.1i) + (-2.0+5.1i) = 8.1i
(2.0+3.1i) - (-2.0+5.1i) = 4.0-2.0i
(2.0+3.1i) * (-2.0+5.1i) = -19.7+3.8i
(2.0+3.1i) / (-2.0+5.1i) = 0.4-0.6i
結尾無空行

輸入樣例2:

1 1 -1 -1.01

輸出樣例2:

(1.0+1.0i) + (-1.0-1.0i) = 0.0
(1.0+1.0i) - (-1.0-1.0i) = 2.0+2.0i
(1.0+1.0i) * (-1.0-1.0i) = -2.0i
(1.0+1.0i) / (-1.0-1.0i) = -1.0

AC code:

#include <stdio.h>
#include <stdlib.h>
void Print_left(float a1, float b1, float a2, float b2, char c);
void Print_result(float res1, float res2);
float round(float num); 

int main(){
	float a1,b1,a2,b2;
	scanf("%f %f %f %f", &a1, &b1, &a2, &b2);
	Print_left(a1, b1, a2, b2, '+');
	Print_result(round(a1 + a2), round(b1 + b2));
	Print_left(a1, b1, a2, b2, '-');
	Print_result(round(a1 - a2), round(b1 - b2));
	Print_left(a1, b1, a2, b2, '*');
	Print_result(round(a1*a2 - b1*b2), round(a1*b2 + a2*b1));
	Print_left(a1, b1, a2, b2, '/'); 
	Print_result(round((a1*a2 + b1*b2) / (a2*a2 + b2*b2)), round((-a1*b2 + a2*b1) / (a2*a2 + b2*b2)));
	return 0;
} 

//打印等號左邊及等號
void Print_left(float a1, float b1, float a2, float b2, char c){
	if(b1 < 0 && b2 < 0) 
    printf("(%0.1f%0.1fi) %c (%0.1f%0.1fi) = ",a1,b1,c,a2,b2);
	else if(b1 < 0) 
    printf("(%0.1f%0.1fi) %c (%0.1f+%0.1fi) = ",a1,b1,c,a2,b2);
	else if(b2 < 0) 
    printf("(%0.1f+%0.1fi) %c (%0.1f%0.1fi) = ",a1,b1,c,a2,b2);
	else 
    printf("(%0.1f+%0.1fi) %c (%0.1f+%0.1fi) = ",a1,b1,c,a2,b2);
}
//打印結果(即打印等號右邊)
void Print_result(float res1, float res2){
	if(res1 == 0 && res2 == 0) 
    printf("0.0\n");
	else if(res1 == 0) 
    printf("%0.1fi\n",res2);
	else if(res2 == 0) 
    printf("%0.1f\n",res1);
	else if(res2 < 0) 
    printf("%0.1f%0.1fi\n",res1,res2);
	else 
    printf("%0.1f+%0.1fi\n",res1,res2);
} 

//四舍五入,要區分正負數的情況 
float round(float num){
	if(num > 0) 
    num = (int)(num*10 + 0.5)/10.0;
	else 
    num = (int)(num*10 - 0.5)/10.0;
	return num;
}

思路:

  • 先計算再四舍五入最后才輸出(保留一位小數),注意四舍五入要區分正負數
  • 復數乘法、除法采用數學公式即可
  • 等號左右式子的格式都需要用if語句區分情況。

參考鏈接

7-37 整數分解為若干項之和 (20 分)

21.12.20

將一個正整數N分解成幾個正整數相加,可以有多種分解方法,例如7=6+1,7=5+2,7=5+1+1,…。編程求出正整數N的所有整數分解式子。

輸入格式:

每個輸入包含一個測試用例,即正整數N (0<N≤30)。

輸出格式:

按遞增順序輸出N的所有整數分解式子。遞增順序是指:對於兩個分解序列N1={n1,n2,⋯}和N2={m1,m2,⋯},若存在i使得n1=m1,⋯,ni=mi,但是ni+1<mi+1,則N1序列必定在N*2序列之前輸出。每個式子由小到大相加,式子間用分號隔開,且每輸出4個式子后換行。

輸入樣例:

7
結尾無空行

輸出樣例:

7=1+1+1+1+1+1+1;7=1+1+1+1+1+2;7=1+1+1+1+3;7=1+1+1+2+2
7=1+1+1+4;7=1+1+2+3;7=1+1+5;7=1+2+2+2
7=1+2+4;7=1+3+3;7=1+6;7=2+2+3
7=2+5;7=3+4;7=7
結尾無空行

AC code:

#include<stdio.h>

int N;
int num[31]; //用數組存放划分結果
int index = -1; //數組的下標(索引),表示拆分項的個數
int sum = 0; //拆分項的累加和,若sum == N則輸出,若sum < N則放棄該遞歸,若sum < N則執行算法和遞歸
int count = 0; //統計輸出的次數,作為輔助,保證輸出格式滿足題目要求,每行最多四個式子
//以上都是全局變量

void division (int x);

int main(){
    scanf("%d", &N);
    division(1); //拆分項從最小正整數 == 1開始,直到滿足num[0] == sum == N
    return 0;
}

void division (int x) {//拆分 
    //輸出部分 
    if (sum == N) {
        count ++;
        printf("%d=", N);
        int j;
        for (j = 0; j < index; j++) 
            printf("%d+", num[j]); //輸出拆分項
        if (count % 4 == 0 || num[index] == N) 
            printf("%d\n", num[index]);
        else 
            printf("%d;", num[index]);
        return 0;
    } 

    //算法主體 
    if (sum > N) {
        return 0;
    }
    for (int i = x; i <= N; i++) { //拆分項從最小正整數 i== 1開始,注意,這里只改變某一項的數值
        num[++index] = i; //在完成全1拆分后,從最后一項 i== 2開始
        sum += i; 
        division (i);//進入遞歸
        sum -= i; //放棄上一個拆分項,以便下一次重新求和
        index --; //在每一個for循環中保持不變,但在進入下一個for時循環執行自減,使數組范圍變小
    } 
}

思路:
如圖所示,以N = 3為例子,可以看到,一開始拆分項全都為1,而后從最后一項 i== 2開始變化,執行這一次的for循環。總共3個黑框代表3個for循環。最后退出循環是因為只有一個division,沒有遞歸直接return回main

參考鏈接




7-38 數列求和-加強版 (20 分)

日期:

21.12.21

給定某數字A(1≤A≤9)以及非負整數N(0≤N≤100000),求數列之和S=A+AA+AAA+⋯+AAANA)。例如A=1, N=3時,S=1+11+111=123。

輸入格式:

輸入數字A與非負整數N

輸出格式:

輸出其N項數列之和S的值。

輸入樣例:

1 3
結尾無空行

輸出樣例:

123
結尾無空行

AC code:

#include <stdio.h>
#include <stdlib.h>
 
int main(){
    int A = 0;
    int N = 0;
    scanf("%d%d", &A, &N);
    int flag = 0; //是否為N+1位數的標志
    int carry = 0; //臨時存放進位數值
    int* num = (int*)malloc(sizeof(int) * (N + 1)); //創建動態數組指針
    if (A == 0 || N == 0) //獨立“0”的特殊情況
        printf("0");
    else{ 
        for (int i = 0; i < N; i++){        
            if (i == 0)
                num[i] = A * N % 10; //個位數值
            else
                num[i] = (A * (N - i) % 10 + carry) % 10; //除個位以外的數值    
            carry = (A * (N - i)  + carry) / 10; //計算進位數值
            if (i == N - 1 && carry != 0){ //N+1位數存在的判斷條件
                num[N] = 1;
                flag = 1;
            }
        }
    }
    if (flag == 1)
        for (int i = N; i >= 0; i--)
            printf("%d", num[i]);
    else
        for (int i = N-1; i >= 0; i--)
            printf("%d", num[i]);
    return 0;
}

思路:

  • 雙長整型也無法保存100,000個數值,所以用動態數組指針

  • A = 0,N = 0 是特殊情況,單獨處理

  • 以計算A = 9,N = 4 為例,計算:9 + 99 + 999 +9,999 = 11,106

    個位:num[0] = 9 * 4 % 10 = A * N % 10 = 6
    個位的進位為carry = (9 * 4 ) / 10 = 3

    十位:num[1] = (9 * 3 % 10 + 3) % 10 = (A * (N - 1) % 10 + carry) % 10 = 0
    十位的進位為carry = (9 * 3 + 3) / 10 = 3

    百位:num[2] = (9 * 2 % 10 + 3) % 10= (A * (N - 2) % 10 + carry) % 10 = 1
    百位的進位為carry = (9 * 2 + 3) / 10 = 2

    千位:num[3] = (9 * 1 % 10 + 2 ) % 10= (A * (N - 3) % 10 + carry) % 10 = 1
    千位的進位為carry = (9 * 1 + 2) / 10 = 1

    萬位:num[4] = 1

  • 可見滿足規律:
    num[i] = (A * (N - i) % 10 + carry) % 10carry = (A * (N - i) + carry) / 10
    結果必為N位或者N+1位數,其中如果有N+1位數其數值必為1

參考鏈接

conclusion

  • 完結了呀
  • 熟悉了c的很多基本操作
  • 期間因為參加六級考試和做csapp的實驗,斷了一個月的訓練時間,不過還是搞定了
  • 是一段艱苦的旅程,但還是很愉快地學到許多技巧
  • 最近在聽佐藤直紀的《異邦人の刃》和《空の涯まで》,以及五月天的《有些事現在不做 一輩子都不會做了》,推薦給你們
    21.12.21


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM