競技プログラミング問題集:基本アルゴリズムの実践

最短区間カバー問題

指定された種類数を満たす最小連続区間を探索する問題です。スライディングウィンドウ手法を用い、要素の出現頻度を動的に管理しながら最適解を導出します。

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

int main() {
    int n, m;
    cin >> n >> m;
    vector<int> cookTypes(n);
    for (int i = 0; i < n; ++i) {
        cin >> cookTypes[i];
    }

    vector<int> freq(2001, 0);
    int uniqueCount = 0;
    int minLength = INT_MAX;
    int left = 0, right = 0;
    int startIdx = -1, endIdx = -1;

    while (right < n) {
        if (freq[cookTypes[right]] == 0) {
            uniqueCount++;
        }
        freq[cookTypes[right]]++;
        right++;

        while (uniqueCount == m && left < right) {
            if (right - left < minLength) {
                minLength = right - left;
                startIdx = left;
                endIdx = right - 1;
            }
            freq[cookTypes[left]]--;
            if (freq[cookTypes[left]] == 0) {
                uniqueCount--;
            }
            left++;
        }
    }

    cout << startIdx + 1 << " " << endIdx + 1 << endl;
    return 0;
}

色付き要素のソート判定

RとBの色を持つ要素をソートし、特定の条件を満たすかを検証します。昇順と降順のソート後、各要素の値と色の関係性をチェックすることで解を判定します。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

struct Element {
    int value;
    char color;
};

bool compareValue(Element a, Element b) {
    if (a.value != b.value) return a.value < b.value;
    return a.color < b.color;
}

int main() {
    int testCases;
    cin >> testCases;
    while (testCases--) {
        int n;
        cin >> n;
        vector<Element> data(n);
        string colorStr;
        for (int i = 0; i < n; ++i) {
            cin >> data[i].value;
        }
        cin >> colorStr;
        for (int i = 0; i < n; ++i) {
            data[i].color = colorStr[i];
        }

        sort(data.begin(), data.end(), compareValue);
        bool isValid = true;
        for (int i = 0; i < n; ++i) {
            if (data[i].value == i + 1) continue;
            if (data[i].value < i + 1 && data[i].color == 'R') continue;
            if (data[i].value > i + 1 && data[i].color == 'B') continue;
            isValid = false;
            break;
        }

        if (isValid) {
            cout << "YES" << endl;
            continue;
        }

        reverse(data.begin(), data.end());
        isValid = true;
        for (int i = 0; i < n; ++i) {
            if (data[i].value == i + 1) continue;
            if (data[i].value < i + 1 && data[i].color == 'R') continue;
            if (data[i].value > i + 1 && data[i].color == 'B') continue;
            isValid = false;
            break;
        }
        cout << (isValid ? "YES" : "NO") << endl;
    }
    return 0;
}

チーム最適化問題

チームの戦力値と要員数の制約下で最大総戦力を求める問題です。要員数で降順ソートし、優先度付きキューを用いて動的に最適なチームを構築します。

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

struct Team {
    long long strength;
    long long requirement;
};

bool compareByRequirement(Team a, Team b) {
    return a.requirement > b.requirement;
}

int main() {
    int n;
    cin >> n;
    vector<Team> teams(n);
    for (int i = 0; i < n; ++i) {
        cin >> teams[i].strength >> teams[i].requirement;
    }

    sort(teams.begin(), teams.end(), compareByRequirement);
    priority_queue<Team, vector<Team>, greater<Team>> minHeap;
    long long totalStrength = 0;
    long long maxTotal = 0;

    for (int i = 0; i < n; ++i) {
        minHeap.push(teams[i]);
        totalStrength += teams[i].strength;
        while (minHeap.size() > teams[i].requirement) {
            totalStrength -= minHeap.top().strength;
            minHeap.pop();
        }
        maxTotal = max(maxTotal, totalStrength);
    }

    cout << maxTotal << endl;
    return 0;
}

電子配置構造生成

原子の電子配置を再帰的に構築する問題です。各能層の電子数を計算し、能層順に整理して出力します。座標変換と再帰呼び出しを用いて効率的に構造を生成します。

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

char orbitalSymbols[] = {'#', 's', 'p', 'd', 'f'};
int maxElectrons[] = {0, 2, 6, 10, 14};
int electronCounts[10][5] = {0};

void buildElectronConfig(int remaining, int level, int sublevel) {
    electronCounts[level][sublevel] = min(maxElectrons[sublevel], remaining);
    int nextRemaining = remaining - maxElectrons[sublevel];
    if (nextRemaining <= 0) return;

    if (sublevel == 1) {
        sublevel = level / 2 + 1;
        level = (level + 1) / 2 + 1;
    } else {
        level++;
        sublevel--;
    }
    buildElectronConfig(nextRemaining, level, sublevel);
}

int main() {
    int totalElectrons;
    cin >> totalElectrons;
    buildElectronConfig(totalElectrons, 1, 1);

    for (int level = 1; ; level++) {
        bool hasElectrons = false;
        for (int sublevel = 1; sublevel <= 4; sublevel++) {
            if (electronCounts[level][sublevel] > 0) {
                cout << level << orbitalSymbols[sublevel] << electronCounts[level][sublevel] << " ";
                hasElectrons = true;
            }
        }
        if (!hasElectrons) return 0;
        cout << endl;
    }
    return 0;
}

ビット演算による最小OR値計算

複数区間のXOR和のOR最小値を求める問題です。高位から低位へ処理し、前缀XORと分割点の可能性を動的に管理することで効率的に解を求めます。

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

int main() {
    long long n, m;
    cin >> n >> m;
    vector<long long> prefixXor(n + 1, 0);
    for (int i = 1; i <= n; ++i) {
        cin >> prefixXor[i];
        prefixXor[i] ^= prefixXor[i - 1];
    }

    vector<bool> validSplit(n + 1, true);
    long long result = 0;

    for (int bit = 62; bit >= 0; --bit) {
        long long countValid = 0;
        for (int i = 1; i <= n; ++i) {
            if (validSplit[i] && !(prefixXor[i] & (1LL << bit))) {
                countValid++;
            }
        }

        if (countValid >= m && !(prefixXor[n] & (1LL << bit))) {
            for (int i = 1; i <= n; ++i) {
                if (prefixXor[i] & (1LL << bit)) {
                    validSplit[i] = false;
                }
            }
        } else {
            result |= (1LL << bit);
        }
    }

    cout << result << endl;
    return 0;
}

タスク割り当て最適化

タスクを複数の工作者に割り当てる際の最短総時間を求めます。二分探索を用いて可能かを判定し、貪欲法で具体的な割り当てを出力します。

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

struct Assignment {
    int start;
    int end;
};

bool canAssign(vector<int>& tasks, int workers, long long maxTime) {
    int currentWorker = 1;
    long long currentLoad = 0;
    for (int task : tasks) {
        if (currentLoad + task > maxTime) {
            currentWorker++;
            currentLoad = task;
            if (currentWorker > workers) return false;
        } else {
            currentLoad += task;
        }
    }
    return true;
}

void printAssignment(vector<int>& tasks, int workers, long long maxTime) {
    vector<Assignment> assignments(workers + 1);
    int currentWorker = workers;
    long long currentLoad = 0;
    for (int i = tasks.size() - 1; i >= 0; --i) {
        if (currentLoad + tasks[i] > maxTime) {
            assignments[currentWorker].end = i + 1;
            currentWorker--;
            currentLoad = tasks[i];
            assignments[currentWorker].start = i + 1;
            assignments[currentWorker].end = i + 1;
        } else {
            currentLoad += tasks[i];
        }
    }
    assignments[1].start = 1;

    for (int i = 1; i <= workers; ++i) {
        if (assignments[i].start == 0) {
            cout << "0 0\n";
        } else {
            cout << assignments[i].start << " " << assignments[i].end << "\n";
        }
    }
}

int main() {
    int tasksCount, workers;
    cin >> tasksCount >> workers;
    vector<int> tasks(tasksCount);
    long long low = 0, high = 0;
    for (int i = 0; i < tasksCount; ++i) {
        cin >> tasks[i];
        low = max(low, (long long)tasks[i]);
        high += tasks[i];
    }

    long long result = high;
    while (low <= high) {
        long long mid = (low + high) / 2;
        if (canAssign(tasks, workers, mid)) {
            result = mid;
            high = mid - 1;
        } else {
            low = mid + 1;
        }
    }

    printAssignment(tasks, workers, result);
    return 0;
}

タグ: sliding-window bitwise-operations binary-search priority-queue depth-first-search

5月31日 19:21 投稿