2.循環結構程序設計

內容

for 循環

  1. 輸出1,2,3,…,n的值
    #include<stdio.h>
    
    int main()
    {
      	int n;
    	scanf("%d", &n);
    	for(int i=1; i<=n; i++){
    		printf("%d\n", i);
    	}
    	return 0;	
    } 
    
    1. for循環的格式: for(初始化; 條件; 調整) 循環體;
    2. 注意理解for循環的執行過程
    3. 在循環內定義變量的i,循環體外不可見。儘量縮小變量定義的範圍。
  2. 輸出所有形如aabb的4位完全平方數。
    #include<stdio.h>
    #include<math.h>
    
    int main()
    {
    	// 兩層循環嵌套 
    	for(int i=1; i<10; i++){
    		for(int j=0; j<10; j++){
    			// 得到 aabb 這種數 
    			int n = i*1100+j*11;
    			// 開方,四捨五入 
    			int m = floor(sqrt(n) + 0.5);
        		if(m*m == n){
    			printf("%d\n", n);
        		} 
    		}
    	}
        return 0;	
    }
    
    1. 僞代碼,實際應用中,不需要太拘泥於僞代碼的格式,主要目標是描述算法梗概,避開細節,啓發思路。
    2. 浮點運算可能存在誤差。在進行浮點數比較時,應考慮到浮點誤差。
    // 解答二 枚舉法
    #include<stdio.h>
    #include<math.h>
    
    int main()
    {
    	for(int i=1; ; i++){
    		int x = i*i;
    		if(x < 1000){
    			continue;
    		}
    		if(x > 9999){
    			break;
    		}
    		int hi = x/100;
    		int low = x%100;
    		if(hi/10 == hi%10  && low/10 == low%10){
    			printf("%d\n", x);
    		}
    	} 
    	return 0;	
    }
    
    注:把所有平方數都列舉出來,選出滿足條件的,四位數,aabb型。

while循環和do-while循環

  1. 3n+1問題
    對於任意大於1的自然數n,若n爲奇數,則將n變爲3n+1,否則變爲n的一半。經過若干次這樣的變換,一定會使n變爲1。求變換次數。
    #include<stdio.h>
    
    int main()
    {
    	int n, times;
    	scanf("%d", &n);
    	times = 0;
    	while(n != 1){
    		if(n%2 == 0){
    			n = n/2;
    		}else{
    			n = 3*n+1;
    		}
    		times++;
    	}
    	printf("%d", times);
    	return 0;	
    }
    
    1. while循環的格式爲:while(條件) 循環體;
    2. 當需要統計某種事物個數時,可以用一個變量來充當計數器。
    3. 不要忘記測試,一個看上去正確的程序可能隱含錯誤。
    4. 在觀察無法找出錯誤時,可以用“輸出中間結果”的方法查錯。
    5. 在32位的系統中,運算器一次最多可以處理32位的數據,寄存器的最大寬度也是32位的。然後, C語言中對數據寬度的規定是指針變量、long型變量的寬度推薦是處理器一次能夠處理的最大寬度,所以就是32位,也就是4個字節。同理,在64位系統中,運算器、寄存器等一次處理的寬度是64位,也就是8個字節,所以指針變量、long型變量等也就都是8個字節了。
  2. 近似計算
    計算Π/4=1-1/3+1/5-1/7+…,直到最後一項小於10的負6次方。
    #include<stdio.h>
    
    int main()
    {
    	double sum = 0;
    	for(int i = 0; ; i++){
        	double term = 1.0/(i*2+1);
    		if(i%2 == 0){
        		sum = sum+term;
        	}else{
        		sum = sum-term;
        	}
        	if(term < 1e-6){
        		break;
        	}
    	}
    	printf("%.6f", sum);
    	return 0;	
    }
    
    do-while語句不常使用。

循環的代價

  1. 階乘之和
    輸入n,計算S=1!+2!+3!+…+n!的末6位(不含前導0)。n<=10的6次方。

    #include<stdio.h>
    
    int main()
    {
        int n;
        scanf("%d", &n);
        int six = 0;
        for(int i = 1; i<=n; i++){
    	    int s = 1;
    	    for(int j =1; j<=i; j++){
    	    	s = s*j;
    	    }
    	    six += s;
        }
        printf("%d", six%1000000);
        return 0;	
    } 
    

    用上面的程序會發現兩個問題:
    - 輸入100時,會出現負數,乘法溢出了。
    - 輸入10的6次方,也會溢出,同時發現太慢了。

    從題目可以知道,他只要最後面的六位,可以在中間結果上每次都取模。

    #include<stdio.h>
    
    int main()
    {
        const int MOD = 1000000;
        int n;
        scanf("%d", &n);
        int six = 0;
        for(int i = 1; i<=n; i++){
        	int s = 1;
        	for(int j =1; j<=i; j++){
        		s = (s*j)%MOD;
        	}
        	six = (six+s)%MOD;
        }
        printf("%d", six);
        return 0;	
    } 
    

    測試幾組數據,發現從40開始,答案始終不變,原因是25!的末尾有6個0,所以從第25項開始,後面所有項都不會影響末尾的6位數字,所以可以放棄使用每次都%一下的方法,只需if(n>25 n=25;)

算法競賽中的輸入輸出框架

  1. 數據統計
    輸入一些整數,求出它們的最小值、最大值和平均值(保留3位小數)。輸入保證這些數都是不超過1000的整數。
    #include<stdio.h>
    
    int main()
    {
        int x, n=0, min, max, s = 0;
        while(scanf("%d", &x) == 1){
    		s += x;
        	if(x < min){
        		min = x;
        	} 
    		if(x > max){
    			max = x;
    		}
        	n++;
    	}
        printf("%d %d %.3f\n", min, max, (double)x/n);
        return 0;	
    }
    
    上面的程序,如何停止輸入呢?
    • windows中,輸入完畢後按Enter鍵,再按ctrl+z鍵,最後再按Enter
    • Linux中,輸入完畢後按Ctrl+D鍵即可結束輸入
    #include<stdio.h>
    #define INF 1000000000
    
    int main()
    {
    	int x, n=0, s = 0, kase=0;
    	while(scanf("%d", &n) == 1 && n){
        	int s = 0, min=INF, max=-INF;
        	for(int i = 0; i<n; i++){
        		scanf("%d", &x);
        		s += x;
        		if(x < min){
        			min = x;
        		} 
        		if(x > max){
        			max = x;
        		}
    		}
    		if(kase) printf("\n");
    		printf("case %d: %d %d %.3f\n", ++kase, min, max, (double)s/n);
    	} 
    	return 0;	
    }
    

練習

  1. 水仙花數
    輸出100-999中的所有水仙花數。若3位數ABC滿足ABC= A3 + B3 + C3,則稱其爲水仙花數。例如153=13+53+33,所以153是水仙花數。
    #include<stdio.h>
    #include<math.h>
    
    int main()
    {
    	int daffodil;
    	for(int i=100; i<=999; i++){
    		int unit = i%10;
    		int tens = i/10%10;
    		int hundreds = i/100;
    		bool j = (i == (int)(pow(unit, 3)+pow(tens, 3)+pow(hundreds, 3)));
    		if(j){
        		printf("%d = %d^3 + %d^3 + %d^3\n", i, hundreds, tens, unit);
    		}
    	}
    
    	return 0;	
    }
    
    這道題沒有難度,主要就是熟悉熟悉循環的運用。
  2. 韓信點兵
    #include<stdio.h>
    
    int main()
    {
    
        while(true){
    	    int a, b, c;
    	    scanf("%d%d%d", &a, &b, &c);
        	bool isExist = false;
        	int i=0;
        	for(i = 10; i<=100; i++){
    		    if(i%3 == a && i%5 == b && i%7 == c){
    			    isExist = true;
    		    	break;
    		    }
    	    }
    	    if(isExist){
    	    	printf("case: %d\n", i);
        	}else{
    	    	printf("case: No answer\n");
    	    }
        }
    
        return 0;
    }
    
    這道題目說“每次只看排尾”,意思也就是隻看餘數,這樣遍歷,一次一次試就可以了,雖然效率不高。
  3. 倒三角形
    #include<stdio.h>
    
    int main()
    {
        int n;
        scanf("%d", &n);
        for(int i=1; i<=n; i++){
        	// 輸出前面的空格 
        	for(int j=1; j<i; j++){
        		printf(" ");
        	}
    	    // 輸出中間的* 
    	    for(int k=1; k<=1+2*(n-i); k++){
    		    printf("*");
    	    }
    	    // 輸出後面的空格 
    	    for(int j=1; j<i; j++){
    		    printf(" ");
    	    }
    	    printf("\n");
        }
        return 0;
    }
    
  4. 子序列的和
    #include<stdio.h>
    #include<math.h>
    
    int main()
    {
        int n, m;
        int sign =0;
        while(scanf("%d%d", &n, &m) == 2){
    	    if(n == 0 && m == 0){
    	    	break;
    	    }
    	    double result;
    	    result = 0;
    	    for(double i=n; i<=m; i++){
    	    	result += 1.0/pow(i,2);
    	    }
    	    sign++;
    	    printf("Case %d: %.5f\n", sign, result);
        }
    
        return 0;
    }
    
    scanf 的返回值爲輸入值的個數。
  5. 分數化小數
    #include<stdio.h>
    
    int main()
    {
        int sign = 0;
        int a, b, c;
        while(scanf("%d%d%d", &a, &b, &c) != EOF){
    	    if(a==0 && b==0 && c==0){
    		    break;
    	    }
    	    double d = 1.0*a/b;
    	    sign++;
    	    printf("Case %d: %.*f", sign, c, d);
    	
        }	
    
        return 0;
    	
    }
    
    注意:%.*f
  6. 排列
    #include<stdio.h>
    
    int main()
    {
        for(int i=1; i<=9; i++){
    	    for(int j=1; j<=9; j++){
    	    	for(int k=1; k<=9; k++){
    	    		if(i==j || i==k || j==k){
    		    		continue;
    		    	}
    		    	int a = i*100+j*10+k*1;
    		    	int b = i*200+j*20+k*2;
    		    	int c = i*300+j*30+k*3;
    		    	if(a<999 && b<999 && c<999){
    		    		printf("%d %d %d\n", a, b, c);
    		    	}
    	    	}
        	}
        }
        return 0;
    }  
    
  7. 猜猜下面程序的輸出內容,一定要運行一遍。
    #include<stdio.h>
    
    int main()
    {
        float i;
        for(i=0; i != 10; i += 0.1){
    	    printf("%.1f\n", i);
        }
        return 0;
    }
    
    double和float都不能用==來比較。根據浮點數的存儲參考IEEE 754浮點數標準
    0.4 ≈ 二進制0.011 001100110011…
    0.3 ≈ 二進制0.0100110011001100…
    0.7 ≈ 二進制0.1011001100110011…
    但是,float只存前幾位,兩個float想加只有前幾位參與操作,損失後面的精度,如果前面的不一樣樣比較就出現不想等,如果都一樣這裏就相等,但你換別的數字試試可能也不相等,原因就是精度損失。
    可以把要比較的兩個數相減的絕對值與一個較小的數比,例如,fabs(f1-f2)<1e-6
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章