アルゴリズム学習ノート:C/C++基礎と基本的なアルゴリズム

1 C/C++の基礎知識

1.1 無限大の定義(INF)

整数型の無限大を表す定数の定義方法:

const int INF = 0x3f3f3f3f;

1.2 scanf関数の使い方

一般的なデータ型のscanfフォーマット指定子:

データ型フォーマット指定子
int%d
long long%lld
float%f
double%lf
char%c
文字列(char配列)%s

1.3 実用的な出力フォーマット

1.3.1 %md

%mdは、int型変数がm桁に満たない場合にm桁で右寄せ出力します。上位桁はスペースで埋められます。変数が既にm桁を超えている場合は、そのまま出力されます。

例:

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

int main() {
    int num1 = 123, num2 = 1234567;
    printf("%5d\n", num1);
    printf("%5d\n", num2);
    return 0;
}

出力結果:

  123
1234567

1.3.2 %0md

変数がm桁に満たない場合、上位桁を0で埋めます。

例:

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

int main() {
    int num1 = 123, num2 = 1234567;
    printf("%05d\n", num1);
    printf("%05d\n", num2);
    return 0;
}

出力結果:

00123
1234567

1.3.3 %.mf

浮動小数点数をm桁の小数部で出力します。四捨六入五成双のルールが適用されます。

例:

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

int main() {
    double value = 12.3456;
    printf("%.0f\n", value);
    printf("%.1f\n", value);
    printf("%.2f\n", value);
    printf("%.3f\n", value);
    printf("%.4f\n", value);
    return 0;
}

出力結果:

12
12.3
12.35
12.346
12.3456

1.4 typedefの使用

long long型を簡略化して定義する例:

typedef long long LL;

1.5 よく使われる数学関数

1.5.1 fabs(double x)

double型変数の絶対値を計算します。

1.5.2 floor(double x), ceil(double x)

それぞれdouble型変数の切り捨てと切り上げを行います。戻り値はdouble型です。

1.5.3 pow(double r, double p)

rのp乗を計算します。両方の引数はdouble型である必要があります。

1.5.4 sqrt(double x)

double型変数の平方根を計算します。

1.5.5 log(double x)

自然対数(底e)の対数を計算します。

注意:C言語には任意の底の対数を直接計算する関数がないため、底の変換公式を使用する必要があります。

log_a(b) = log_e(b) / log_e(a)

1.5.6 round(double x)

double型変数xを四捨五入します。戻り値もdouble型なので、整数型に変換する必要があります。

注:コード中でif(n)はif(n!=0)と同じ意味、if(!n)はif(n==0)と同じ意味です。

1.6 breakとcontinue文

break:必要な場面でループを直接終了します。

continue:条件が満たされた場合にcontinueを実行すると、それ以降の処理をスキップし、次のループ反復に進みます。

1.7 配列の注意点

サイズsizeの1次元配列を定義した場合、アクセスできるのはインデックス0からsize-1までの要素です。例えば、int a[10]ではa[0], a[1], ..., a[9]にアクセスできますが、a[10]にはアクセスできません。

1.8 memset関数

配列の全要素に同じ値を設定する関数:

memset(配列名, 値, sizeof(配列名));

配列の全要素を0に設定する例:

memset(配列名, 0, sizeof(配列名));

1次元配列と2次元配列の両方に適用できます。

1.9 strlen()関数

文字配列内の最初の\0までの文字数を取得します。

strlen(文字配列);

2 アルゴリズム入門

2.1 シミュレーション

シミュレーションとは、問題の記述に従ってそのままコードを書く手法で、特定のアルゴリズムをあまり必要とせず、問題の記述に基づいてコーディング能力を試すものです。

2.1.1 コラッツの予測(3n+1問題)

#include <iostream>
using namespace std;

int main() {
    int number, count = 0;
    cin >> number;
    
    while (number != 1) {
        if (number % 2 == 0)
            number = number / 2;
        else
            number = (3 * number + 1) / 2;
        count++;
    }
    
    cout << count << endl;
    return 0;
}

2.1.2 学校別得点集計

#include <iostream>
using namespace std;

const int MAX_SCHOOLS = 100010;
int schoolScores[MAX_SCHOOLS] = {0};

int main() {
    int numEntries, schoolId, score;
    cin >> numEntries;
    
    for (int i = 0; i < numEntries; i++) {
        cin >> schoolId >> score;
        schoolScores[schoolId] += score;
    }
    
    int topSchool = 1, maxScore = -1;
    for (int i = 1; i <= numEntries; i++) {
        if (schoolScores[i] > maxScore) {
            maxScore = schoolScores[i];
            topSchool = i;
        }
    }
    
    cout << topSchool << " " << maxScore << endl;
    return 0;
}

2.2 要素の検索

2.2.1 配列内の要素検索

#include <iostream>
using namespace std;

const int MAX_SIZE = 210;
int arr[MAX_SIZE];

int main() {
    int size, target;
    while (cin >> size) {
        for (int i = 0; i < size; i++) {
            cin >> arr[i];
        }
        
        cin >> target;
        int position;
        for (position = 0; position < size; position++) {
            if (arr[position] == target) {
                cout << position << endl;
                break;
            }
        }
        
        if (position == size) {
            cout << -1 << endl;
        }
    }
    return 0;
}

2.3 図形の出力

2.3.1 正方形パターンの出力

#include <iostream>
using namespace std;

int main() {
    int width, height;
    char symbol;
    cin >> width >> symbol;
    
    height = (width % 2 == 1) ? width / 2 + 1 : width / 2;
    
    // 上辺の出力
    for (int i = 0; i < width; i++) {
        cout << symbol;
    }
    cout << endl;
    
    // 中間部分の出力
    for (int i = 1; i < height - 1; i++) {
        cout << symbol;
        for (int j = 0; j < width - 2; j++) {
            cout << " ";
        }
        cout << symbol << endl;
    }
    
    // 下辺の出力(高さが1より大きい場合のみ)
    if (height > 1) {
        for (int i = 0; i < width; i++) {
            cout << symbol;
        }
        cout << endl;
    }
    
    return 0;
}

2.4 日付処理

2.4.1 日付間の差分計算

#include <iostream>
using namespace std;

int monthDays[13][2] = {
    {0, 0}, {31, 31}, {28, 29}, {31, 31}, {30, 30},
    {31, 31}, {30, 30}, {31, 31}, {31, 31}, {30, 30},
    {31, 31}, {30, 30}, {31, 31}
};

bool isLeapYear(int year) {
    return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}

int main() {
    int date1, date2;
    while (cin >> date1 >> date2) {
        if (date1 > date2) {
            int temp = date1;
            date1 = date2;
            date2 = temp;
        }
        
        int y1 = date1 / 10000, m1 = date1 % 10000 / 100, d1 = date1 % 100;
        int y2 = date2 / 10000, m2 = date2 % 10000 / 100, d2 = date2 % 100;
        
        int daysDiff = 1;
        while (y1 < y2 || m1 < m2 || d1 < d2) {
            d1++;
            if (d1 == monthDays[m1][isLeapYear(y1)] + 1) {
                m1++;
                d1 = 1;
            }
            if (m1 == 13) {
                y1++;
                m1 = 1;
            }
            daysDiff++;
        }
        
        cout << daysDiff << endl;
    }
    return 0;
}

2.5 基数変換

2.5.1 基数変換のテンプレート

P進数の数xをQ進数に変換するには、2段階のプロセスが必要です。

(1)P進数xを10進数yに変換

int y = 0, product = 1;
while (x != 0) {
    y = y + (x % 10) * product;
    x = x / 10;
    product = product * P;
}

(2)10進数yをQ進数zに変換

int z[40], digitCount = 0;
do {
    z[digitCount++] = y % Q;
    y = y / Q;
} while (y != 0);

2.5.2 D進数でのA+B

#include <iostream>
using namespace std;

int main() {
    int a, b, base;
    cin >> a >> b >> base;
    
    int sum = a + b;
    int result[31], numDigits = 0;
    
    do {
        result[numDigits++] = sum % base;
        sum /= base;
    } while (sum != 0);
    
    for (int i = numDigits - 1; i >= 0; i--) {
        cout << result[i];
    }
    
    return 0;
}

2.6 文字列処理

2.6.1 回文判定

#include <iostream>
#include <cstring>
using namespace std;

const int MAX_LENGTH = 256;

bool isPalindrome(char str[]) {
    int len = strlen(str);
    for (int i = 0; i < len / 2; i++) {
        if (str[i] != str[len - 1 - i]) {
            return false;
        }
    }
    return true;
}

int main() {
    char inputStr[MAX_LENGTH];
    while (cin.getline(inputStr, MAX_LENGTH)) {
        if (isPalindrome(inputStr)) {
            cout << "YES" << endl;
        } else {
            cout << "NO" << endl;
        }
    }
    return 0;
}

3 基本的なアルゴリズム

3.1 ソート

ソート問題では、通常C++のsort関数を使用します。ソート問題の一般的な解決手順:

3.1.1 関連する構造体の定義

ソート問題では、通常、個人の多くの情報(名前、試験番号、スコア、順位など)が与えられます。これらの情報を格納するために構造体配列を使用できます。例:

struct Student {
    char name[10];    // 名前
    char id[10];      // 試験番号
    int score;        // スコア
    int rank;         // 順位
} students[100010];

3.1.2 比較関数の作成

sortを使用してソートする際には、ソートルールを実装するcmp関数を提供する必要があります。例えば、以下の要件:すべての学生をスコアの降順でソートし、スコアが同じ場合は名前の辞書順で昇順にソートします。

実際のソートルールは次のように記述できます:

  1. 2人の学生のスコアが異なる場合、スコアが高い学生を前に配置
  2. そうでない場合、辞書順で小さい名前の学生を前に配置

対応するcmp関数:

bool compareStudents(Student a, Student b) {
    if (a.score != b.score)
        return a.score > b.score;
    else
        return strcmp(a.name, b.name) < 0;
}

注:strcmp(str1, str2)は、str1の辞書順がstr2より小さい場合は負数、等しい場合は0、大きい場合は正数を返します。

3.1.3 順位の実装

多くのソート問題では、ソート後に各個人の順位を計算する必要があります。一般的なルールは:スコアが異なる場合は順位も異なり、スコアが同じ場合は同じ順位で、その順位を占有します。例えば、5人の学生のスコアが90、88、88、88、86の場合、順位は1、2、2、2、5となります。

実装方法:

まず、配列の最初の個人の順位を1とします。次に、残りの個人を走査します。現在の個人のスコアが前の個人のスコアと等しい場合、現在の個人の順位は前の個人の順位と同じになります。そうでない場合、現在の個人の順位は配列のインデックス+1になります。

students[0].rank = 1;
for (int i = 1; i < n; i++) {
    if (students[i].score == students[i-1].score) {
        students[i].rank = students[i-1].rank;
    } else {
        students[i].rank = i + 1;
    }
}

タグ: C++ アルゴリズム プログラミング基礎 データ構造 文字列処理

7月30日 08:28 投稿