天天看點

【【2022.12.7每天四個簡單題之簡單題大智慧系列1】(遞增序列,連續的奇數和,完全數,質數)】遞增數列連續奇數的和完全數質數

最近做題時有了一些感悟,有一些簡單題看似簡單,其中卻也有一些奇特之處,甚至有些我也想了好久才想明白,當然,我是一名剛剛接觸程式設計幾個月的小菜,自然不及社群裡的各路大神,現在你也看到了,我做的題甚至可能入不了您的法眼。我在做題的同時,我也遇到了一些小點搞不明白想搜尋卻很難搜尋到答案,可能是大佬們覺得這些問題太簡單了不想回答吧!可是卻真的困擾我很久,是以我整理了一下一些菜到不能再菜的小題,在幫助我做筆記的同時,也希望可以幫助到大家!

遞增數列

讀取一系列的整數 X,對于每個 X,輸出一個 1,2,…,X 的序列。

輸入格式

輸入檔案中包含若幹個整數,其中最後一個為 0,其他的均為正整數。

每個整數占一行。

對于輸入的正整數,按題目要求作輸出處理。

對于最後一行的整數 0,不作任何處理。

輸出格式

對于每個輸入的正整數 X,輸出一個從 1 到 X 的遞增序列,每個序列占一行。

資料範圍

1≤X≤100

輸入樣例:

5

10

3

輸出樣例:

1 2 3 4 5

1 2 3 4 5 6 7 8 9 10

1 2 3

ok,相信大家都覺得,诶呀真的很簡單诶,直接将它for循環不就可以了嗎?沒錯,我們做這個題的時候,隻需要在輸入每個數後,将它從小到大for循環出來就可以啦,就像這樣:

隻不過當時我的問題是:如何才能一直輸入呢?可能現在回想覺得這個問題很幼稚,可是這确實是困擾我相當長時間的問題,我也查了很多的資料可是就是覺得不是很滿意,隻不過現在我知道了可以這樣:

while(scanf("%d",&n)&&n)/*當然以此題來說,一直輸入到輸入為零停止,
&&前面是輸入的形式,後面是表示n==0時結束

           

當然這個可能還是不滿意,嗯…&&後面的那個n還是感覺有點抽象,是以我們還可以這樣:

while(cin>>n,0)
/*因為用了 while(cin>>n,n)相當于當輸入n等于0時,表達式為:
while(cin>>n(逗号表達式不運作這裡),0)
——> while(0)
——>while(false)也就是不循環

           

哦,另外提一嘴,雖然是c++了,cin,cout雖然用起來十分友善,可是我們如果要是競賽的話,scanf與printf在資料很大的情況下要比cin,cout快好幾倍,如果你不使用萬能頭檔案,那就需要用這個:

好了,這就是這個題我學到了那些東西hh,現在是這個題的答案啦:

#include<cstdio>
#include<iostream>
using namespace std;
int main()
{
    int n;
    while(scanf("%d",&n)&&n){//因為用了 while(cin>>n,n)相當于當輸入n等于0時,表達式為while(cin>>n(逗号表達式不運作這裡),0)——> while(0)——>while(false) 不循環
        for(int i=1;i<=n;i++) cout<<i<<' ';
     cout<<endl;
    }
    return 0;
}
           

連續奇數的和

輸入 N 對整數對 X,Y,對于每對 X,Y,請你求出它們之間(不包括 X 和 Y)的所有奇數的和。

輸入格式

第一行輸入整數 N,表示共有 N 對測試資料。

接下來 N 行,每行輸入一組整數 X 和 Y。

輸出格式

每對 X,Y 輸出一個占一行的奇數和。

資料範圍

1≤N≤100,

−1000≤X,Y≤1000

輸入樣例:

7

4 5

13 10

6 4

3 3

3 5

3 4

3 8

輸出樣例:

11

5

12

哦吼!乍一眼一看,這不就是一個和嗎,直接來個for循環不就好了,嗯是滴,這個題确實挺簡單,不過作為初學者,我們經常有一些丢三落四的情況,比如:

剛開始的代碼:

#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
    int n,x,y,sum=0;
    scanf("%d",&n);
    while(n--){
        scanf("%d %d",&x,&y);
        if(x>y){
            swap(x,y);
        }
        for(int j=x+1;j<y;j++){
            if(j%2){
                sum+=j;
            }
           
        }
        printf("%d\n",sum);
    }
    return 0;
}
           

當時我檢查了半天竟然沒檢查出來什麼錯誤,最後一比較一看,咦——這是一個要循環多次的for循環,每一次循環要輸出一個答案,然後在下一次循環時重新輸入此時與上次循環無關,而我的sum還儲存着上次的結果!這樣結果肯定不對啊,是以我們應該在最後的輸出後面加一個語句讓sum清零,于是代碼更新:

#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
    int n,x,y,sum=0;
    scanf("%d",&n);
    while(n--){
        scanf("%d %d",&x,&y);
        if(x>y){
            swap(x,y);
        }
        for(int j=x+1;j<y;j++){
            if(j%2){
                sum+=j;
            }
           
        }
        printf("%d\n",sum);
        sum=0;
    }
    return 0;
}
           

結果當然是accep。

雖然這隻是一個小題,但是到了最後我們學習到資料結構時,一些連結清單和動态記憶體的釋放,也就是我們的free函數,很多人在冗長的代碼下就會忘記最後的釋放記憶體,這與我們這個忘記最後sum清零的本質是一樣的

完全數

一個整數,除了本身以外的其他所有約數的和如果等于該數,那麼我們就稱這個整數為完全數。

例如,6 就是一個完全數,因為它的除了本身以外的其他約數的和為 1+2+3=6。

現在,給定你 N 個整數,請你依次判斷這些數是否是完全數。

輸入格式

第一行包含整數 N,表示共有 N 個測試用例。

接下來 N 行,每行包含一個需要你進行判斷的整數 X。

輸出格式

每個測試用例輸出一個結果,每個結果占一行。

如果測試資料是完全數,則輸出 X is perfect,其中 X 是測試資料。

如果測試資料不是完全數,則輸出 X is not perfect,其中 X 是測試資料。

資料範圍

1≤N≤100,

1≤X≤108

輸入樣例:

3

6

5

28

輸出樣例:

6 is perfect

5 is not perfect

28 is perfect

這個題題意很好了解,就是說判斷一個數可不可以被它的約數相加結果相同,是以一開始我們就想到了這種做法:

#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
    int n,m,sum=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&m);
        for(int j=1;j<m;j++){
            if(m%j==0){
                sum+=j;
            }
        }
        if(sum==m){
            printf("%d is perfect\n",m);
        }else{
            printf("%d is not perfect\n",m);
        }
        sum=0;
    }
    return 0;
}
           

然而結果卻Time Limit Exceeded了也就是逾時

這是為什麼呢? 那是因為當m取到10的8次方,時間複雜度是O(N*m)就逾時了,那我們應該怎麼辦呢?

我們可以發現,當我們這裡的j * j大于m時後面的就可以不用看了,原因是我們假設mid的平方=m,可以将0到m分成左右兩段,則 j <m時 m/ j就是那個比mid還大的m的約數(在mid分成兩段的右半段),這樣隻用走根号m次循環,每次循環判斷兩次,如果要是 m%j==0了,那麼m/j也一定是另外的一個約數,隻需要判斷它是否與j相同即可(存在平方根,即6 * 6=36的形式,六是等于六的,故重複加兩個六沒有意義)

好了,啰嗦了這麼多,舉個例子吧:

比如因為六的平方是三十六,故6将36分成兩段(6就是mid),是以我們的j隻需要從1枚舉到6即可,然後當j取3的時候(假設),在得出3是其中一個約數的同時,還可以順便算出12也是它的約數,而3與12正好一個在6的左邊一個在6的右邊,這就用一步算出來了兩個約數

用代碼實作就是這樣:

#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
    int n,m,sum=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&m);
        for(int j=1;j*j<m;j++){
            if(m%j==0){
            
                sum+=j;
                
                if(m/j<m&&m/j!=m){
                    sum+=m/j;
                }
            }
        }
        if(sum==m){
            printf("%d is perfect\n",m);
        }else{
            printf("%d is not perfect\n",m);
        }
        sum=0;
    }
    return 0;
}
           

質數

一個大于 1 的自然數,如果除了 1 和它自身外,不能被其他自然數整除則稱該數為質數。

例如 7 就是一個質數,因為它隻能被 1 和 7 整除。

現在,給定你 N 個大于 1 的自然數,請你依次判斷這些數是否是質數。

輸入格式

第一行包含整數 N,表示共有 N 個測試資料。

接下來 N 行,每行包含一個自然數 X。

輸出格式

每個測試用例輸出一個結果,每個結果占一行。

如果測試資料是質數,則輸出 X is prime,其中 X 是測試資料。

如果測試資料不是質數,則輸出 X is not prime,其中 X 是測試資料。

資料範圍

1≤N≤100,

1<X≤107

輸入樣例:

3

8

51

7

輸出樣例:

8 is not prime

51 is not prime

7 is prime

質數問題也是老生常談了,隻不過對于我們新手來說,代碼經常會寫的十分冗長,不但容易出錯而且還不容易查找,這個題目一共有兩個點:

  • 1.輸入一個數n,并在其後輸入n個不同的數
  • 2.布爾一個值,并令其判斷每個數是否是質數

    代碼如下:

#include<cstdio>
#include<iostream>
using namespace std;
int main()
{
    int n;
    cin >> n;
    while (n -- )
    {
        int p;
        cin >> p;

        bool is_prime = true;
        for (int i = 2; i * i <= p; i ++ )
            if (p % i == 0)
            {
                is_prime = false;
                break;
            }

        if (is_prime) printf("%d is prime\n", p);
        else printf("%d is not prime\n", p);
    }

    return 0;
}
           

對于新手而言還是要反複默寫,争取把代碼寫的簡潔明了!

繼續閱讀