OI入門:基本文法とアルゴリズムの基礎

第一章:環境構築から始めよう

プログラミングを始める前に、まず開発環境を整える必要があります。OI(情報学オリンピック)では、Dev-C++、Code::Blocks、VS CodeなどのIDEが一般的です。初心者はDev-C++から始めることをおすすめします。軽量で設定も簡単なため、初心者には適しています。

1.1 Dev-C++のインストール手順

  1. 「Dev-C++」を検索し、公式サイトを確認してください(広告に注意)。
  2. ダウンロード後、インストーラーを実行し、デフォルト設定でインストールします。
  3. インストール完了後、ファイル→新規→ソースファイルを開き、準備完了です。

1.2 最初のプログラム:「Hello World」

#include <iostream>

using namespace std;

int main()
{
    cout << "Hello OI! I'm coming!" << endl;
    return 0;
}

このコードは、標準出力を使って「Hello OI! I'm coming!」と表示するものです。各要素について説明します:

  • #include <iostream>:入出力機能を提供するライブラリを読み込みます。
  • using namespace std;:標準名前空間を使用することを宣言します。
  • int main():プログラムのエントリーポイントです。
  • cout << ... << endl;:画面に出力します。
  • return 0;:正常終了を示します。

第二章:C++の基本文法

2.1 変数の使い方

変数とはデータを格納するための箱のようなものです。以下のように定義できます:

int a;        // 整数型
double b;     // 浮動小数点型
char c;       // 文字型
bool d;       // 真偽値型

初期化は以下の通りです:

int a = 10;
double b = 3.14159;
char c = 'O';
bool d = true;

2.2 データ型の種類

用途 範囲
int 整数 -20億〜20億 10、-500
long long 大きな整数 -9e18〜9e18 12345678901234
double 浮動小数点(倍精度) 約15桁 3.14、0.0001
float 浮動小数点(単精度) 約6桁 1.23f
char 文字 ASCIIコード 'A'、'5'、'+'
bool 真偽値 true/false true

2.3 演算子

算術演算子

int x = 10, y = 3;
cout << x + y << endl;  // 13
cout << x - y << endl;  // 7
cout << x * y << endl;  // 30
cout << x / y << endl;  // 3(整数除算)
cout << x % y << endl;  // 1(剰余)

代入演算子

int a = 5;
a += 3;  // a = a + 3
a -= 2;  // a = a - 2
a *= 4;  // a = a * 4
a /= 6;  // a = a / 6
a %= 3;  // a = a % 3

比較演算子

int x = 5, y = 8;
cout << (x > y) << endl;  // false(0)
cout << (x < y) << endl;  // true(1)
cout << (x == y) << endl; // false(0)
cout << (x != y) << endl; // true(1)
cout << (x >= y) << endl; // false(0)
cout << (x <= y) << endl; // true(1)

論理演算子

bool a = true, b = false;
cout << (a && b) << endl;  // false(0)
cout << (a || b) << endl;  // true(1)
cout << (!a) << endl;      // false(0)

2.4 入出力

#include <iostream>
using namespace std;

int main()
{
    int a;
    double b;
    char c;
    cout << "入力してください:" << endl;
    cin >> a >> b >> c;
    cout << "整数:" << a << endl;
    cout << "小数:" << b << endl;
    cout << "文字:" << c << endl;
    return 0;
}

2.5 分岐構造

if文

#include <iostream>
using namespace std;

int main()
{
    int score;
    cout << "点数を入力:" << endl;
    cin >> score;
    if (score >= 90)
        cout << "優秀!" << endl;
    else if (score >= 80)
        cout << "良好!" << endl;
    else if (score >= 60)
        cout << "合格!" << endl;
    else
        cout << "不合格!" << endl;
    return 0;
}

switch文

#include <iostream>
using namespace std;

int main()
{
    int day;
    cout << "曜日を入力(1-7):" << endl;
    cin >> day;
    switch (day)
    {
        case 1: cout << "月曜日" << endl; break;
        case 2: cout << "火曜日" << endl; break;
        case 3: cout << "水曜日" << endl; break;
        case 4: cout << "木曜日" << endl; break;
        case 5: cout << "金曜日" << endl; break;
        case 6: cout << "土曜日" << endl; break;
        case 7: cout << "日曜日" << endl; break;
        default: cout << "無効" << endl;
    }
    return 0;
}

2.6 繰り返し構造

for文

#include <iostream>
using namespace std;

int main()
{
    for (int i = 1; i <= 10; i++)
        cout << i << " ";
    return 0;
}

while文

#include <iostream>
using namespace std;

int main()
{
    int sum = 0, n = 0;
    while (sum <= 1000)
    {
        n++;
        sum += n;
    }
    cout << "n=" << n << "で和が1000を超える" << endl;
    return 0;
}

do-while文

#include <iostream>
using namespace std;

int main()
{
    int x = 0;
    do
    {
        cout << "Hello" << endl;
        x++;
    } while (x > 1);
    return 0;
}

2.7 配列

int a[10];      // 10個の整数
double b[5];    // 5個の小数
char c[20];     // 20個の文字

2.8 文字列

#include <string>
using namespace std;

int main()
{
    string s1;          // 空文字列
    string s2 = "OI";   // 初期化
    string s3 = s2;     // コピー
    string s4(5, 'a');  // "aaaaa"
    return 0;
}

2.9 関数

#include <iostream>
using namespace std;

int add(int x, int y)
{
    return x + y;
}

int main()
{
    int a = 5, b = 3;
    int sum = add(a, b);
    cout << "5+3=" << sum << endl;
    return 0;
}

2.10 ポインタ

#include <iostream>
using namespace std;

int main()
{
    int a = 10;
    int *p = &a;
    cout << "aの値:" << a << endl;
    cout << "pの値:" << p << endl;
    cout << "*pの値:" << *p << endl;
    *p = 20;
    cout << "変更後のa:" << a << endl;
    return 0;
}

第三章:アルゴリズム入門

3.1 総当たり法

#include <iostream>
using namespace std;

int main()
{
    cout << "3と5で割り切れる数:" << endl;
    for (int i = 1; i <= 100; i++)
        if (i % 3 == 0 && i % 5 == 0)
            cout << i << " ";
    return 0;
}

3.2 検索アルゴリズム

線形探索

#include <iostream>
using namespace std;

int search(int a[], int n, int target)
{
    for (int i = 0; i < n; i++)
        if (a[i] == target) return i;
    return -1;
}

int main()
{
    int a[5] = {10, 20, 30, 40, 50};
    int pos = search(a, 5, 30);
    if (pos != -1) cout << "見つかった:" << pos << endl;
    return 0;
}

二分探索

#include <iostream>
using namespace std;

int binary_search(int a[], int n, int target)
{
    int l = 0, r = n - 1;
    while (l <= r)
    {
        int mid = (l + r) / 2;
        if (a[mid] == target) return mid;
        else if (a[mid] > target) r = mid - 1;
        else l = mid + 1;
    }
    return -1;
}

int main()
{
    int a[5] = {10, 20, 30, 40, 50};
    int pos = binary_search(a, 5, 40);
    if (pos != -1) cout << "見つかった:" << pos << endl;
    return 0;
}

3.3 ソートアルゴリズム

バブルソート

#include <iostream>
using namespace std;

void bubble_sort(int a[], int n)
{
    for (int i = 0; i < n - 1; i++)
        for (int j = 0; j < n - 1 - i; j++)
            if (a[j] > a[j + 1])
            {
                int temp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = temp;
            }
}

int main()
{
    int a[5] = {3, 1, 4, 2, 5};
    bubble_sort(a, 5);
    for (int i = 0; i < 5; i++) cout << a[i] << " ";
    return 0;
}

選択ソート

#include <iostream>
using namespace std;

void select_sort(int a[], int n)
{
    for (int i = 0; i < n - 1; i++)
    {
        int min_pos = i;
        for (int j = i + 1; j < n; j++)
            if (a[j] < a[min_pos]) min_pos = j;
        int temp = a[i];
        a[i] = a[min_pos];
        a[min_pos] = temp;
    }
}

int main()
{
    int a[5] = {3, 1, 4, 2, 5};
    select_sort(a, 5);
    for (int i = 0; i < 5; i++) cout << a[i] << " ";
    return 0;
}

挿入ソート

#include <iostream>
using namespace std;

void insert_sort(int a[], int n)
{
    for (int i = 1; i < n; i++)
    {
        int temp = a[i];
        int j = i - 1;
        while (j >= 0 && a[j] > temp)
        {
            a[j + 1] = a[j];
            j--;
        }
        a[j + 1] = temp;
    }
}

int main()
{
    int a[5] = {3, 1, 4, 2, 5};
    insert_sort(a, 5);
    for (int i = 0; i < 5; i++) cout << a[i] << " ";
    return 0;
}

3.4 再帰と反復

再帰

#include <iostream>
using namespace std;

int fib(int n)
{
    if (n == 1 || n == 2) return 1;
    return fib(n - 1) + fib(n - 2);
}

int main()
{
    int n;
    cout << "nを入力:" << endl;
    cin >> n;
    cout << "fib(" << n << ")=" << fib(n) << endl;
    return 0;
}

反復

#include <iostream>
using namespace std;

int main()
{
    int n;
    cout << "nを入力:" << endl;
    cin >> n;
    if (n == 1 || n == 2)
    {
        cout << "1" << endl;
        return 0;
    }
    int a = 1, b = 1, c;
    for (int i = 3; i <= n; i++)
    {
        c = a + b;
        a = b;
        b = c;
    }
    cout << c << endl;
    return 0;
}

3.5 前処理和(累積和)

#include <iostream>
using namespace std;

int main()
{
    int n, m;
    cout << "配列長とクエリ数:" << endl;
    cin >> n >> m;
    int a[1000], s[1001] = {0};
    for (int i = 0; i < n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) s[i] = s[i - 1] + a[i - 1];
    for (int i = 0; i < m; i++)
    {
        int l, r;
        cin >> l >> r;
        cout << "区間[" << l << "," << r << "]の和:" << s[r + 1] - s[l] << endl;
    }
    return 0;
}

第四章:実践問題

4.1 最大公約数(GCD)

#include <iostream>
using namespace std;

int gcd(int a, int b)
{
    while (b != 0)
    {
        int temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}

int main()
{
    int a, b;
    cout << "2つの整数を入力:" << endl;
    cin >> a >> b;
    cout << "最大公約数:" << gcd(a, b) << endl;
    return 0;
}

4.2 素数判定

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

bool is_prime(int n)
{
    if (n <= 1) return false;
    if (n == 2) return true;
    if (n % 2 == 0) return false;
    for (int i = 3; i <= sqrt(n); i += 2)
        if (n % i == 0) return false;
    return true;
}

int main()
{
    int n;
    cout << "整数を入力:" << endl;
    cin >> n;
    if (is_prime(n)) cout << n << "は素数" << endl;
    else cout << n << "は素数ではない" << endl;
    return 0;
}

4.3 二次元累積和

#include <iostream>
using namespace std;

const int N = 1010;
int a[N][N], s[N][N];

int main()
{
    int n, m, q;
    cout << "行数、列数、クエリ数:" << endl;
    cin >> n >> m >> q;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            cin >> a[i][j];
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            s[i][j] = a[i][j] + s[i-1][j] + s[i][j-1] - s[i-1][j-1];
    while (q--)
    {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        int sum = s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1];
        cout << "和:" << sum << endl;
    }
    return 0;
}

4.4 フィボナッチ数列(再帰と反復)

#include <iostream>
using namespace std;

int main()
{
    int n;
    cout << "階数を入力:" << endl;
    cin >> n;
    if (n == 1 || n == 2)
    {
        cout << "1" << endl;
        return 0;
    }
    int a = 1, b = 1, c;
    for (int i = 3; i <= n; i++)
    {
        c = a + b;
        a = b;
        b = c;
    }
    cout << c << endl;
    return 0;
}

第五章:避けるべき落とし穴と進歩の方向

5.1 常見なミス

  • 分号の欠落
  • 中括弧の不一致
  • 変数の初期化不足
  • 配列の境界外アクセス
  • 整数のオーバーフロー

5.2 デバッグテクニック

  • coutによる変数出力
  • コメントによるコードの切り分け
  • 境界値のテスト
  • コンパイラエラーの解釈

5.3 進級のステップ

  • 基礎:スタック、キュー、連結リスト
  • 中級:ツリー、ヒープ、Union-Find
  • 上級:セグメント木、BIT、グラフアルゴリズム

第六章:まとめ

この記事を通じて、C++の基本文法やアルゴリズムの基礎知識を学びました。今後は、これらの知識をもとに実践的な問題を解いていくことで、さらにスキルを磨いていきましょう。

タグ: C++ アルゴリズム プログラミング OI 情報学オリンピック

7月14日 03:29 投稿