1. 程式人生 > 其它 >C++語法入門刷題筆記(二)

C++語法入門刷題筆記(二)

1.acwing725.完全數

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

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

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

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

// 注意這樣時間複雜度太大,會超時
#include <cstdio>

using namespace std;

int main()
{
	int n;
	scanf("%d", &n);
	while (n--)
	{
		int x,sum = 0;
		scanf("%d", &x);
		for (int i = 1;i < x;i++)
		{
			if (x%i == 0) sum += i;
		}
		if (sum == x) printf("%d is perfect\n",x);
		else
		{
			printf("%d is not perfect\n",x);
		}
		
	}
	
	return 0;
}

// 優化版本
#include <cstdio>
using namespace std;
int main()
{
	int n;
	scanf("%d", &n);
	while (n--)
	{
		int x,sum = 0;
		scanf("%d", &x);
		for (int i = 1;i*i <= x;i++)// 減少迴圈次數
		{
			if (x%i == 0)
            {
                if (i < x) sum += i;
                if (i != x/i && x/i < x) sum += x/i; // 關鍵步驟
            }
		}
		if (sum == x) printf("%d is perfect\n",x);
		else
		{
			printf("%d is not perfect\n",x);
		}		
	}	
	return 0;
}

特別解法

有點數學基礎的人都應該知道100000000內的完全數沒有幾個……

數學部分
100000000100000000內的完全數有6,28,496,8128,335503366,28,496,8128,33550336.所以說多背一點數字是很有用的

既然這道題可以直接O(1)O(1)解決,我們不妨來說一下完全數的各種性質以備於各種毒瘤的演算法競賽.

完全數比較重要的幾個性質
(也是我只知道的幾個性質)

所有完全數都是三角形數
目前截止發現的所有完全數都以66或2828結尾
到現在為止,數學家們一共發現了4848個完全數,且4848個完全數全部是偶數
如果有人們沒有找到的奇完全數,則它一定可以寫成12p+112p+1或36p+936p+9的形式,而且pp是素數
奇完全數一定大於1030010300
完全數的約數的倒數之和為調和數
完全數可以表示成連續奇數的立方和
完全數可以表示成22的連續自然數的次冪之和,且這些自然數的數量必定是素數
完全數計演算法
若2p−1是素數(亦稱其為梅森素數),則2p−1∗(2p−1)是完全數.

時間複雜度
這裡資料小了一點,對於每個資料時間複雜度為O(1)O(1).
資料再大我都不怕,反正現在找到48個不如個map然後對映一個布林類不就好了!

#include <bits/stdc++.h>
using namespace std;

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        if (n == 6 || n == 28 || n == 496 || n == 8128 || n == 33550336)  
            cout << n << " is perfect" << endl;
        else cout << n << " is not perfect" << endl;
    }

    return 0;

}

2.錯誤小提示

// 錯誤寫法,第二句不會執行,很容易犯錯
if (...) xxx; xxx;
// 正確寫法
1.if (...) {xxx; xxx;}
2.if (...)
{
    xxx; xxx;
}
3.if() xxx,xxx;

3.acwing727.菱形

#include <cstdio>
#include <algorithm>
using namespace std;

int main()
{
	int n;
	scanf("%d", &n);
	for (int i = 0;i < n;i++)
	{
		for (int j = 0;j < n;j++)
		{
			if (abs(i-n/2) + abs(j-n/2) <= n/2)// abs函式求絕對值
			{
				printf("*");
			}
			else printf(" ");// 漏掉這裡就只能列印右半邊圖形
		}
		printf("\n");
	}
	
	return 0;
}

大致思路:來自於y總講解。根據圖形特點,這是一個正方形中的菱形,離正方形中心的曼哈頓距離<= n/2的點列印*,其他點列印空格。

例如在平面上,座標(x1,y1)的i點與座標(x2,y2)的j點的曼哈頓距離為:

d(i,j)=|X1-X2|+|Y1-Y2|.

4.高精度浮點數運算問題

如果進行了一系列對浮點數的運算,精度可能變得不準確,這時運算之前、之後的浮點數可能不相等,但實際上是相等的,只是因為精度丟失。看例子:

int a = 3;
if (sqrt(3) * sqrt(3) != 3) puts("!!!");

結果我們會發現顯示!!!,說明精度丟失了,但實際結果應該相等。

解決方案如下:

// 當兩個浮點數相差足夠小時,我們可以認為他們相等
const double eps = 1e-6;
int a = 3;
if (fabs(sqrt(3) * sqrt(3) - 3) < eps) puts("相等");
// double fabs(double x),fabs用於求絕對值

這時我們便會發現顯示相等了。

5.陣列的初始化

注意一下:

// 將陣列全部初始化為0的寫法
int f[10] = {0};// 很常用

切記:定義在函式內部(包括main函式)的陣列,如果不初始化,則是隨機的。

知識點:放在函式內部的陣列空間存放在棧裡,如果棧的空間不夠,則可能會出現段錯誤。

但放在函式外部的陣列空間存放在堆裡,只要不超過記憶體限制,可以定的比較大。比如可以放main函式之外。

定義在函式外部的陣列,如果不初始化,則是全部預設為0的。

這就是區域性變數和全域性變數的區別。

注意陣列下標越界的問題,會導致段錯誤!

6.練習題.旋轉陣列

輸入一個n,再輸入n個整數。將這個陣列順時針旋轉k(k <= n)次,最後將結果輸出。

// 一般做法,有兩重迴圈
int a[100];
int n,k;
cin >> n >> k;
for (int i = 0;i < n;i++) cin >> a[i];

while (k--)
{
	int t = a[n-1];
	for (int i = n-2;i >= 0;i--)
	{
		a[i+1] = a[i];
	}
	a[0] = t;
}
for (int i = 0;i < n;i++) cout << a[i] << ' ';
// 更巧妙的做法,只有一重迴圈
//reverse函式用於反轉在[first,last)範圍內的順序(包括first指向的元素,不包括last指向的元素),reverse函式沒有返回值

int a[100];

int n,k;
cin >> n >> k;

for (int i = 0;i < n;i++) cin >> a[i];
reverse(a,a + n);// 翻轉a[0]到a[n-1]的n個數
reverse(a,a + k);// 翻轉a[0]到a[k-1]的k個數
reverse(a + k,a + n);

注意:reverse函式的引數是左閉右開的。

//演示reverse函式
int a[3] = {0,1,2},b[3] = {0,1,2};
	reverse(a,a+1);// 翻轉1個數
	reverse(b,b+2);// 翻轉2個數
	for (int i = 0;i < 3;i++) cout << a[i] << endl;
	cout << endl;
	for (int i = 0;i < 3;i++) cout << b[i] << endl;

//輸出如下
0
1
2
    
1
0
2

7.acwing743.陣列中的行

輸入一個二維陣列M[12][12],根據輸入的要求,求出二維陣列中某一行的元素的平均值或元素的和。

輸入格式

第一行輸入整數L,表示所求的具體行數(行數從0開始計數)。

第二行包含一個大寫字母,若為’S’,則表示需要求出第 L 行的元素的和,若為’M’,則表示需要求出第 L 行的元素的平均值。

接下來12行,每行包含12個用空格隔開的浮點數,表示這個二維陣列,其中第 i+1 行的第 j+1 個數表示陣列元素M[i][j]。輸出一個數,表示所求的平均數或元素的和的值,保留一位小數。

#include <iostream>

using namespace std;

int main()
{
    int l;
    char op;
    cin >> l >> op;
    double s=0;
    for(int i=0;i<12;i++)
    {
        for(int j=0;j<12;j++)
        {
            double a;
            cin >> a;
            if(i==l) s+=a;
        }
    }

    printf("%.1lf",op=='S' ? s : s/12);//簡化操作
}

8.acwing749.陣列的上方區域

#include <iostream>
using namespace std;
int main()
{
    char c;
    cin>>c;
    double a,res=0;
    for(int i=0;i<12;i++)
        for(int j=0;j<12;j++)
        {
            cin>>a;
            if(j>i&&i+j<11)res+=a;//關鍵是找到綠色方塊的分佈規律,兩塊區域的交集
        }
    printf("%.1lf",c=='S'?res:res/30);
}

9.acwing753.平方矩陣 I

// 這題不會做,困難
// 主要思路就是求到上下左右四條邊的最小值,找到規律就不難
// y總解法
#include <iostream>
#include <algorithm>

using namespace std;

int main()
{
    int n,up,down,left,right;
    while (cin >> n,n)
    {
        for (int i = 1;i <= n;i++)
        {
            for (int j = 1;j <= n;j++)
            {
                up = i;down = n-i+1;left = j;right = n-j+1;// 這四個變數還可以省略
                cout << min(min(up,down),min(left,right)) << ' ';
            }
            cout << endl;
        }
        cout << endl;
    }
    return 0;
}

10.acwing756.蛇形矩陣

// 來自於微軟面試題,嘗試寫了一小段,沒完成
// 有一定難度
// y總解題思路
#include <iostream>

using namespace std;

int res[100][100];// 小細節,定義為全域性變數預設初始化為0
int main()
{
    int n,m;
    cin >> n >> m;
    
    int dx[] = {0,1,0,-1},dy[] = {1,0,-1,0};// 定義橫縱座標偏移量
    for (int x = 0,y = 0,d = 0,k = 1;k <= n*m;k++)
        // d用於確定前進方向,開始時向右走
    {
        res[x][y] = k;
        int a = x + dx[d],b = y + dy[d];
        if (a < 0 || a >= n || b < 0 || b >= m || res[a][b])
        // 判斷是否出界,或者已經遍歷過,未遍歷過res[a][b] == 0
        // 符合條件則將方向順時針旋轉90度
        {
            d = (d + 1)%4;
            a = x + dx[d],b = y + dy[d];
        }
        x = a,y = b;
    }
    for (int i = 0;i < n;i++)
    {
        for (int j = 0;j < m;j++)
        {
            cout << res[i][j] << ' ';
        }
        cout << endl;
    }
    return 0;
}
// 題解2,紫書
#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

const int maxn = 150;
int a[maxn][maxn];

int main() 
{
    int n, m;
    cin >> n >> m;

    memset(a, 0, sizeof(a));// 初始化陣列a為0
    // 作用是在一段記憶體塊中填充某個給定的值,它是對較大的結構體或陣列進行清零操作的一種最快方法
    int x = 0, y = 0;       //初始座標座標,(0,0) 
    int cnt = 1;            //初始化第一個數 
    a[x][y] = cnt;

    while (cnt < n * m ) 
    {   
        //用下一筆的位置來判斷
        //向右, 符合條件,則填入下一筆。____提前預判  
        while (y + 1 < m && !a[x][y + 1]) a[x][ ++ y] = ++ cnt;
        //向下 
        while (x + 1 < n && !a[x + 1][y]) a[ ++ x][y] = ++ cnt;
        //向左
        while (y - 1 >= 0 && !a[x][y - 1]) a[x][ -- y] = ++ cnt;
        //向上
        while (x - 1 >= 0 && !a[x - 1][y]) a[ -- x][y] = ++ cnt;    
    }
    for (int i = 0; i < n; i ++ ) 
    {
        for (int j = 0; j < m; j ++ )
        {
            cout << a[i][j] << " ";
        }   
        cout << endl;
    } 

    return 0;
}