Southwest University for Nationalities 2023 Programming Competition Selection Problems and Solutions

L1-1 Thank You, Karl!

This problem requires outputting a specific formatted string. The output contains an emoticon with escaped backslashes.

Reference Implementation

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

int main()
{
    cout << "Thank You Karl!\\\\(>_<)/" << endl;
    return 0;
}

L1-2 It's Fantasy Time!

Given two integers representing quantities, compute their ratio and display it with exactly three decimal places. The solution involves a simple division operation formatted to three decimal precision.

Reference Implementation

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

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int numerator, denominator;
    cin >> numerator >> denominator;
    printf("%.3f", static_cast<double>(numerator) / denominator);
    
    return 0;
}

L1-3 Are You Here to Help Klee Fish?

Given multiple integer values, compute a cumulative score based on the range each value falls into:

Range Points
[0, 99] 1
[100, 199] 2
[200, 299] 5
[300, 399] 10
400+ 15

Reference Implementation

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

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int count;
    cin >> count;
    int totalScore = 0;
    
    while (count--)
    {
        int value;
        cin >> value;
        
        if (value < 100) totalScore += 1;
        else if (value < 200) totalScore += 2;
        else if (value < 300) totalScore += 5;
        else if (value < 400) totalScore += 10;
        else totalScore += 15;
    }
    
    cout << totalScore << endl;
    return 0;
}

L1-4 Minesweeper Game

Given a grid representation of a Minesweeper board where * represents a mine and . represents an empty cell, generate an output grid where each non-mine cell displays the count of adjacent mines. Cells with zero adjacent mines display ..

Approach

For each cell, if it's a mine, preserve the *. Otherwise, count mines in all 8 neighboring positions and display that count. Adjacent positions are checked using direction arrays.

Reference Implementation

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

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int rows, cols;
    cin >> rows >> cols;
    vector<string> input(rows), result(rows);
    
    for (int i = 0; i < rows; i++)
        cin >> input[i];
    
    int dx[8] = {-1, -1, -1, 0, 0, 1, 1, 1};
    int dy[8] = {-1, 0, 1, -1, 1, -1, 0, 1};
    
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
        {
            if (input[i][j] == '*')
            {
                result[i][j] = '*';
            }
            else
            {
                int adjacentMines = 0;
                for (int dir = 0; dir < 8; dir++)
                {
                    int ni = i + dx[dir];
                    int nj = j + dy[dir];
                    if (ni >= 0 && ni < rows && nj >= 0 && nj < cols && 
                        input[ni][nj] == '*')
                        adjacentMines++;
                }
                result[i][j] = (adjacentMines == 0) ? '.' : ('0' + adjacentMines);
            }
        }
    }
    
    for (int i = 0; i < rows; i++)
        cout << result[i] << '\n';
    
    return 0;
}

L1-5 Slime

Given a string of lowercase letters, remove all duplicate occurrences keeping only the first appearance of each character. Output the count of unique characters and the resulting string.

Approach

Maintain a boolean array tracking which characters have been seen. Iterate through the string, and for each character not yet seen, append it to the result and mark it as seen.

Reference Implementation

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

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int length;
    cin >> length;
    string input;
    cin >> input;
    
    vector<bool> seen(26, false);
    string output;
    
    for (int i = 0; i < length; i++)
    {
        int charIndex = input[i] - 'a';
        if (!seen[charIndex])
        {
            seen[charIndex] = true;
            output.push_back(input[i]);
        }
    }
    
    cout << output.length() << '\n' << output << '\n';
    return 0;
}

L1-6 Encrypted Communication

Given a string and an array of weights for each letter, perform a Caesar cipher rotation by k positions (rotated part moves to front). Then replace each character with its corresponding weight from the given array.

Approach

First, reduce rotation amount modulo string length. Split the string into two parts at position n-k and rearrange. Then map each character to its weight using a lookup table.

Reference Implementation

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

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int length, rotation;
    cin >> length >> rotation;
    string message;
    cin >> message;
    
    rotation %= length;
    
    vector<int> weight(26);
    for (int i = 0; i < 26; i++)
        cin >> weight[i];
    
    string rotated;
    for (int i = length - rotation; i < length; i++)
        rotated.push_back(message[i]);
    for (int i = 0; i < length - rotation; i++)
        rotated.push_back(message[i]);
    
    for (char c : rotated)
        cout << weight[c - 'a'];
    cout << '\n';
    
    return 0;
}

L1-7 Character Operations

A string of length n undergoes k operations. Operation type 1 swaps two characters at given positions. Operation type 2 toggles whether the string should be reversed (first n and last n characters swapped). Track the state efficiently using a flag to avoid actual swapping during operations.

Approach

Use a boolean flag to track whether the logical string is in reversed state. When performing swap operations, adjust indices based on the current state. At the end, output the string in its current or reversed form based on the flag.

Reference Implementation

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

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n, k;
    string data;
    cin >> n >> data >> k;
    
    bool reversed = false;
    
    while (k--)
    {
        int type, pos1, pos2;
        cin >> type >> pos1 >> pos2;
        pos1--; pos2--;
        
        if (type == 1)
        {
            if (reversed)
            {
                if (pos1 < n) pos1 += n;
                else pos1 -= n;
                if (pos2 < n) pos2 += n;
                else pos2 -= n;
            }
            swap(data[pos1], data[pos2]);
        }
        else
        {
            reversed = !reversed;
        }
    }
    
    if (reversed)
        cout << data.substr(n) << data.substr(0, n) << '\n';
    else
        cout << data << '\n';
    
    return 0;
}

L1-8 Vivo 50!

Calculate and rank students based on a composite score. Skip students with failing grades. Sort by total score descending, then by name ascending for ties. Output the top k ranked students.

Scoring Formula

  • Morality score: min(100, 70 + de)
  • Academic score: f * 10 + 50
  • Total: (academic + zhi) * 0.7 + morality * 0.3

Reference Implementation

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

struct Student {
    string name;
    double total;
    int rank;
};

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n, k;
    cin >> n >> k;
    vector<Student> candidates;
    
    while (n--)
    {
        string name;
        double five, de, zhi, gg;
        cin >> name >> five >> de >> zhi >> gg;
        
        if (gg == 0) continue;
        
        double morality = min(100.0, 70.0 + de);
        double academic = five * 10.0 + 50.0;
        double total = (academic + zhi) * 0.7 + morality * 0.3;
        
        candidates.push_back({name, total, 0});
    }
    
    sort(candidates.begin(), candidates.end(), [](const Student& a, const Student& b) {
        if (a.total != b.total) return a.total > b.total;
        return a.name < b.name;
    });
    
    for (size_t i = 0; i < candidates.size(); i++)
    {
        candidates[i].rank = (i == 0 || candidates[i].total != candidates[i-1].total) 
                             ? i + 1 : candidates[i-1].rank;
    }
    
    for (const auto& s : candidates)
    {
        if (s.rank > k) break;
        printf("%d %s %.1lf\n", s.rank, s.name.c_str(), s.total);
    }
    
    return 0;
}

L2-1 Gaming Circle

Use Union-Find (Disjoint Set Union) to track connected components. Answer connectivity queries and count the number of distinct sets after processing all connections.

Approach

Initialize each node as its own parent. Process union operations to merge sets. For each query, check if two nodes belong to the same set. Finally, count roots to determine the number of connected components.

Reference Implementation

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

class UnionFind {
    vector<int> parent, rank;
public:
    UnionFind(int size) : parent(size + 1), rank(size + 1, 0) {
        iota(parent.begin(), parent.end(), 0);
    }
    
    int find(int x) {
        if (parent[x] != x) parent[x] = find(parent[x]);
        return parent[x];
    }
    
    void unite(int x, int y) {
        int px = find(x), py = find(y);
        if (px == py) return;
        if (rank[px] < rank[py]) swap(px, py);
        parent[py] = px;
        if (rank[px] == rank[py]) rank[px]++;
    }
    
    bool connected(int x, int y) {
        return find(x) == find(y);
    }
    
    int countSets() {
        unordered_set<int> roots;
        for (size_t i = 1; i < parent.size(); i++)
            roots.insert(find(i));
        return roots.size();
    }
};

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n, m, q;
    cin >> n >> m >> q;
    
    UnionFind uf(n);
    
    while (m--)
    {
        int x, y;
        cin >> x >> y;
        uf.unite(x, y);
    }
    
    while (q--)
    {
        int a, b;
        cin >> a >> b;
        cout << (uf.connected(a, b) ? "yes" : "no") << '\n';
    }
    
    cout << uf.countSets() << '\n';
    return 0;
}

L2-2 Problem Set Assembly

Assign problems to 10 different problem sets based on availability and difficulty requirements. Each problem has a difficulty level. Track available slots and assign problems to matching or adjacent difficulty sets.

Approach

Parse problem descriptions to extract difficulty and index. Maintain queues for each difficulty level. First assign problems to exact difficulty slots, then for remaining slots, try adjacent difficulties. Sort assigned problems and output as required format.

Reference Implementation

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

struct ProblemAssignment {
    int setNum, problemIndex;
    bool operator<(const ProblemAssignment& other) const {
        if (setNum != other.setNum) return setNum < other.setNum;
        return problemIndex > other.problemIndex;
    }
};

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n;
    cin >> n;
    
    vector<int> available(11);
    for (int i = 1; i <= 10; i++)
        cin >> available[i];
    
    vector<vector<ProblemAssignment>> assigned(11);
    queue<ProblemAssignment> waiting[11];
    
    for (int contestant = 1; contestant <= n; contestant++)
    {
        int problemCount;
        cin >> problemCount;
        
        for (int j = 0; j < problemCount; j++)
        {
            string code;
            cin >> code;
            
            int pos = 1;
            int diff = 0, idx = 0;
            while (code[pos] >= '0' && code[pos] <= '9') {
                diff = diff * 10 + (code[pos] - '0');
                pos++;
            }
            pos++;
            while (code[pos] >= '0' && code[pos] <= '9') {
                idx = idx * 10 + (code[pos] - '0');
                pos++;
            }
            
            if (available[diff] > 0) {
                available[diff]--;
                assigned[diff].push_back({diff, idx});
            } else {
                waiting[diff].push({diff, idx});
            }
        }
    }
    
    for (int d = 1; d <= 10; d++)
    {
        while (available[d] > 0)
        {
            bool placed = false;
            for (int higher = d + 1; higher <= 10 && !placed; higher++) {
                if (!waiting[higher].empty()) {
                    assigned[higher].push_back(waiting[higher].front());
                    waiting[higher].pop();
                    available[d]--;
                    placed = true;
                }
            }
            if (placed) continue;
            for (int lower = d - 1; lower >= 1 && !placed; lower--) {
                if (!waiting[lower].empty()) {
                    assigned[lower].push_back(waiting[lower].front());
                    waiting[lower].pop();
                    available[d]--;
                    placed = true;
                }
            }
            if (!placed) break;
        }
    }
    
    vector<ProblemAssignment> finalList;
    for (int d = 1; d <= 10; d++) {
        sort(assigned[d].begin(), assigned[d].end());
        for (const auto& p : assigned[d])
            finalList.push_back(p);
    }
    
    for (size_t i = 0; i < finalList.size(); i++) {
        if (i) cout << ' ';
        cout << finalList[i].setNum << '-' << finalList[i].problemIndex;
    }
    
    return 0;
}

L2-3 Simple Counting

Given a sequence of n single-digit numbers, count the number of ways to combine them using two operations: addition modulo 10 and multiplication modulo 10. Find the count of sequences ending with each digit from 0 to 9.

Approach

Dynamic programming where dp[i][d] represents the number of ways the first i numbers can produce digit d. For each new number, update states by combining with all previous possible digits using both operations.

Referance Implementation

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

const int MOD = 998244353;
const int MAX_DIGITS = 10;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n;
    cin >> n;
    
    vector<vector<int>> dp(n + 1, vector<int>(MAX_DIGITS, 0));
    
    int firstDigit;
    cin >> firstDigit;
    dp[1][firstDigit] = 1;
    
    for (int i = 2; i <= n; i++)
    {
        int current;
        cin >> current;
        for (int prev = 0; prev < MAX_DIGITS; prev++)
        {
            int addResult = (current + prev) % MAX_DIGITS;
            int mulResult = (current * prev) % MAX_DIGITS;
            dp[i][addResult] = (dp[i][addResult] + dp[i-1][prev]) % MOD;
            dp[i][mulResult] = (dp[i][mulResult] + dp[i-1][prev]) % MOD;
        }
    }
    
    for (int d = 0; d < MAX_DIGITS; d++)
        cout << dp[n][d] << '\n';
    
    return 0;
}

L2-4 Homecoming Day

Each city has an unlock day. Starting from the school city, find the earliest day you can reach each city by traveling through connected roads. The travel day to a connected city is the maximum of you're arrival day and that city's unlock day.

Approach

Use BFS starting from the school city. Maintain the earliest arrival day for each city. For each neighbor, compute the required day as the maximum of current arrrival day and neighbor's unlock day. Update if this results in an earlier arrival time.

Reference Implementation

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

struct Edge {
    int to;
    int next;
};

const int MAX_NODES = 500000;
const int INF = 0x3f3f3f3f;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n, m, start;
    cin >> n >> m >> start;
    
    vector<int> unlockDay(n + 1);
    for (int i = 1; i <= n; i++) {
        int day;
        cin >> day;
        unlockDay[day] = i;
    }
    
    vector<Edge> edges(2 * MAX_NODES);
    vector<int> head(n + 1, -1);
    int edgeCount = 0;
    
    auto addEdge = [&](int u, int v) {
        edges[edgeCount] = {v, head[u]};
        head[u] = edgeCount++;
    };
    
    for (int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        addEdge(u, v);
        addEdge(v, u);
    }
    
    vector<int> earliest(n + 1, INF);
    queue<int> bfs;
    earliest[start] = unlockDay[start];
    bfs.push(start);
    
    int dx[8] = {-1, -1, -1, 0, 0, 1, 1, 1};
    int dy[8] = {-1, 0, 1, -1, 1, -1, 0, 1};
    
    while (!bfs.empty()) {
        int current = bfs.front();
        bfs.pop();
        
        for (int e = head[current]; e != -1; e = edges[e].next) {
            int neighbor = edges[e].to;
            int required = max(earliest[current], unlockDay[neighbor]);
            
            if (required < earliest[neighbor]) {
                earliest[neighbor] = required;
                bfs.push(neighbor);
            }
        }
    }
    
    for (int i = 1; i <= n; i++) {
        if (i > 1) cout << ' ';
        cout << earliest[i];
    }
    
    return 0;
}

Tags: programming competition algorithms Data Structures Union-Find Dynamic Programming

Posted on Sat, 01 Aug 2026 16:26:03 +0000 by bender