1. 程式人生 > >C學習筆記(一)-程式設計作業

C學習筆記(一)-程式設計作業

C學習筆記(一)-程式設計作業

Attention:* 本部落格是個人學習筆記,若存在問題,歡迎指出!
作業平臺: Matrix

第十一週作業:

[Loop]雙基迴文數

Description

如果一個正整數n至少在兩個不同的進位制b1、b2下都是迴文數(2 <= b1, b2 <= 10),則稱n為雙基迴文數(注意:迴文數不能包含前導0)。輸入非負整數S < 10^6,輸出比S大的最小雙基迴文數。
【輸入資料第一行是樣例個數T(1<=T<=50),接下來有T行,每行有一個非負整數S(小於10 ^6)】
【輸出資料有T行,每行輸出對應的最小雙基迴文數】

解答過程中遇到的問題註釋在程式碼裡

#include<stdio.h>

int changePosition(int num, const int n);
int isPalindrome(int a[], const int n);


int main(){
    int n = 0, i = 0, j = 0, flag = 0;
    int num1 = 0;
    
    scanf("%d", &n);
    int num[50] = {0};
    
    for(i = 0; i < n; i++){
        scanf("%d"
, &num[i]); } for(i = 0; i < n; i++){ for(num1 = num[i]+1;;num1++){ //把num陣列中的值拎出來賦值給別的,對陣列(按地址引用)進行操作易出現問題 flag = 0; for(j = 2; j <= 10; j++){ if(changePosition(num1, j)) flag++; } if(flag >= 2
){ printf("%d\n", num1); break; } } } return 0; } int changePosition(int num, int n){ int a[50]; int i = 0; while(num){ a[i++] = num%n; num /= n; } if(isPalindrome(a, i)){ //這裡傳i過去就好 //在轉進位制的函式中同時進行比較,方便對其位數i進行操作! return 1; }else{ return 0; } } int isPalindrome(int a[], const int n){ int j = 0; for(j = 0; j < n/2; j++){ if(a[j] != a[n-j-1]){ return 0; } } return 1; }

[Loop]校門外的樹

這道題不難,但是要注意細節:迴圈的條件,題目的要求等;
以及malloc之後要free()。

Description
某校大門外長度為 L 的馬路上有一排樹,每兩棵相鄰的樹之間的間隔都是1米。
我們可以把馬路看成一個數軸,馬路的一端在數軸0 的位置,另一端在L 的位置;
數軸上的每個整數點,即0,1,2,……,L,都種有一棵樹。
由於馬路上有一些區域要用來建地鐵。這些區域用它們在數軸上的起始點和終止點表示。已知任一區域的起始點和終止點的座標都是整數,區域之間可能有重合的部分。現在要把這些區域中的樹(包括區域端點處的兩棵樹)移走。你的任務是計算將這些樹都移走後,馬路上還有多少棵樹。


Input
輸入的第一行有兩個整數L(1 <= L <= 10000)和 M(1 <= M <= 100),L 代表馬路 的長度,M 代表區域的數目,L 和M 之間用一個空格隔開。接下來的M 行每行包含兩個不 同的整數,用一個空格隔開,表示一個區域的起始點和終止點的座標。


Output
輸出包括一行,這一行只包含一個整數,表示馬路上剩餘的樹的數目。

#include<stdio.h>
#include<stdlib.h>

int main(){
    int l, m, i = 0, j = 0, sumtree = 0;
    
    scanf("%d%d", &l, &m);
    int area[100][2];
    for(i = 0; i < m; i++){
        scanf("%d%d", &area[i][0], &area[i][1]);
    }
    
    int* tree = (int*)malloc((l+1)*sizeof(int));
    for(i = 0; i <= l; i++){
        tree[i] = 1;
    }
    
    for(i = 0; i < m; i++){
        for(j = area[i][0]; j <= area[i][1]; j++){
            tree[j] = 0;
        }
    }
    
    for(i = 0; i <= l; i++){
        sumtree = sumtree + tree[i];
    }
    
    printf("%d\n", sumtree);
    free(tree);
    
    return 0;
}

[Algorithm]約瑟夫環

這道題同樣不難,同樣是要注意細節!所有有迴圈的題都應該多注意迴圈終止的條件!!!

Description:
在羅馬人佔領喬塔帕特後,39 個猶太人與Josephus及他的朋友躲到一個洞中,39個猶太人決定寧願死也不要被敵人抓到,於是決定了一個自殺方式,41個人排成一個圓圈,由第1個人開始報數,每報數到第3人該人就必須自殺,然後再由下一個重新報數,直到所有人都自殺身亡為止。(死亡的人會從環中消失,例如3號自殺,那麼當報數再次報到2號時,會直接跳過3號,4號繼續報數)
現在,我們把這個問題變化一下,假設現在有n個人(最多不超過1000人),編號為1到n,他們排成一圈從1號人開始報數, 報數時報到m的人自殺(M不會超過10)問最後一個自殺的人是誰?


Input
一行,兩個整數,n和m,分別為人數和編號


Output
一行,一個數,最後死的人的編號

#include<stdio.h>
#include<stdlib.h>

int main(){
    int n, m;
    scanf("%d%d", &n, &m);
    int i = 0, count = 0, flag = 0;
    
    int* people = (int*)malloc((n+1)*sizeof(int));
    for(i = 0; i <= n; i++){
        people[i] = 1;
    }
    
    while(flag < n-1){ 
    // 以後命名注意帶有含義,我在這裡一直用m除錯了好久才找到問題
    // 以及迴圈終止條件為只剩一個人,所以這樣比較合適
        for(i = 1; i <= n; i++){
            if(people[i] == -1) continue;
            count += 1;
            if(count%m == 0){
                people[i] = -1;
                count = 0;
                flag += 1;
            }
        }
    }
    
    for(i = 1; i <= n; i++){
        if(people[i]!=-1) printf("%d\n", i);
    }
    
    free(people);
    return 0;
}

[Recursion] 漢諾塔

幫助理解遞迴!

Description
漢諾塔:漢諾塔(又稱河內塔)問題是源於印度一個古老傳說的益智玩具。大梵天創造世界的時候做了三根金剛石柱子,在一根柱子上從下往上按照大小順序摞著64片黃金圓盤。大梵天命令婆羅門把圓盤從下面開始按大小順序重新擺放在另一根柱子上。並且規定,在小圓盤上不能放大圓盤,在三根柱子之間一次只能移動一個圓盤。(百度百科)


Input
給出第一根柱子的圓盤個數N(N <= 20),你要做的就是找出最快將圓盤全部移到第三根柱子的方法,並將方法輸出出來。


Output
如當N=3的時候,輸出
32
13
21
23
13
每行兩個數字表示將第一個數字的柱子的最上面的圓盤移動到第二個數字所表示的柱子上。

#include<stdio.h>
void hanoi(const int, const int, const int, int);

int main(){
    int n;
    scanf("%d", &n);
    
    hanoi(1,2,3,n);
    
    return 0;
}

void hanoi(const int from, const int mid, const int to, int n){
    if(n == 1){
        printf("%d%d\n", from, to);
    }
    else{
    //把 n-1 號盤子移動到中間
        hanoi(from, to, mid, n-1);
    //把1號從起點移到終點 
        hanoi(from, mid, to, 1);
    //然後把中間的的n-1號盤子也移到終點
        hanoi(mid, from, to, n-1);
    }
}

所以說一共就三步:

  1. 把 n-1 號盤子移動到緩衝區
  2. 把1號從起點移到終點
  3. 然後把緩衝區的n-1號盤子也移到終點

所以寫成py程式碼就是:

def move(n,from,buffer,to)move(n,from,buffer,to)
   if n==1:
       print('Move',n,'from',from,'to',to)
   else:
       move(n-1,from,to,buffer)
       move(1,from,buffer,to)
       move(n-1,buffer,from,to)
  1. 要從a到b 那c就是緩衝 move(n-1,from,to,buffer)
  2. 要從a到c 那b就是緩衝 move(1,from,buffer,to)
  3. 要從b到c 那a就是緩衝 move(n-1,buffer,from,to)

來源:醬紫君(知乎)
連結:https://www.zhihu.com/question/24385418/answer/282940567

[Algorithm]紀念郵票

!!一道我以為簡單但是有時間限制於是要優化演算法的題

Description
郵局最近推出了一套特殊的紀念郵票,這套郵票共有N張,郵票面值各不相同,按編號順序為1分,2分,......,N分。
小杭是個集郵愛好者,他很喜歡這套郵票,可惜現在他身上只有M分,並不夠把全套都買下。他希望儘量買,最好剛好花光所有錢。作為一個集郵愛好者,小杭也不想買的郵票編號斷斷續續。所以小杭打算買面值a分至b分的b-a+1張連續的郵票,且總價值剛好為M分。
你的任務是求出所有符合要求的方案,以[a,b]的形式輸出。


Input
輸入檔案只有一行,包含兩個數N和M(1<=N,M<= 1,000,000,000)


Output
輸出檔案每行包含一個合法方案:[a,b]
按a值從小到大輸出。
輸出檔案不含任何空格。

先放一下我stupid的演算法:

#include<stdio.h>

int main(){
    int n, m;
    scanf("%d%d", &n, &m);
    int sum = 0, i = 0, j = 0;
    
    for(i = 1; i <= n; i++){
        sum = 0;
        for(j = i; j <= n, sum <= m; j++){
            sum = sum +j;
            if(sum == m){
                printf("[%d,%d]\n", i, j);
            }
        }
    }
    
    return 0;
}
//input:999999999 999999999的時候,算的特別緩慢

優化:(參考)
連續的郵票即等差數列,滿足: ( a + b ) ( b a + 1 ) / 2 = = m (a+b)(b-a+1)/2== m ( a + b ) ( b a + 1 ) = = 2 m (a+b)(b-a+1) == 2m 即存在多少對數x=(a+b), y=(b-a+1)使其相乘為2m即可:
解得a=(x-y+1)/2, b=(x+y-1)/2; y從1到sqrt(2m), 再判斷b<n && x*y==2m

#include<stdio.h>
#include<math.h>

int main()
{
    int  n,m;
    scanf("%d%d",&n,&m);
    int i = 0, a = 0, b = 0;
  	int x = 0, y = 0;
    
    for(i = sqrt(2*m) ; i>=1; i--){
        if((2*m)%i==0){
          	x = 2*m/i;
          	y = i;
            a=(x-y+1)/2;
            b=(x+y-1)/2;
            if(b<=n&&(a+b)*(b-a+1)==2*m)
                printf("[%d,%d]\n",a,b);
        }
    }
    return 0;
}

[algorithm]完數問題

和上一個題一樣,這道題也需要對演算法進行優化

Description
編寫程式,輸入一個正整數M,輸出[1,M]內的所有“完數”。每輸出一個數(包括最後一個數)均以換行結束。所謂“完數”,是指一個數恰好等於它的因子值(因子不包括該數本身)之和。例如6是完數,因為6=1+2+3。6<=M<=100000。


Sample Input
100
Sample Output
6
28

before:

#include<stdio.h>

int main(){
    int m = 0, i = 0, j = 0, sum = 0;
    scanf("%d", &m);
    
    for(i = 1; i <= m; i++){
        sum = 0;
        for(j = 1; j <= i/2; j++){
            if(i%j == 0){
                sum = sum + j;
            }
        }
        if(sum == i){
            printf("%d\n", i);
        }
    }
    
    return 0;
}

after:(優化補充在註釋裡)

#include<stdio.h>
#include<math.h>

int main(){
    int m = 0, i = 0, j = 0, sum = 0;
    scanf("%d", &m);
    
    for(i = 6; i <= m; i++){
        sum = 0;
        //主要優化在j<=sqrt(i),
        //若數a為非平方數,它的大於1小於a的因數成對出現,每一對中的較小因數要小於a的平方根;
        //不能從j=1開始,j=1時對應的是i本身
        for(j = 2; j <= sqrt(double(i)); j++){
            if(i%j == 0){
                sum = sum + j + i/j;
                //這裡也要做變化
                //還要注意若一個數為i的平方根,則只加一次
                if(i == j*j) sum = sum - j;
            }
        }
        //這裡給sum補+1
        if(sum+1 == i){
            printf("%d\n", i);
            i += i;
        }
    }
    
    return 0;
}

[Algorithm] 兔子問題