Algorithm Solutions: Path Search, String Ranking, and Knapsack Problems

D - Path Traversal

A straightforward depth-first search approach can solve this traversal problem.

int nodes, edges, max_steps, min_cost, max_cost;
vector<int> valid_endpoints;
vector<pair<int, int>> graph[MAX_NODES];

void traverse(int current_node, int current_cost, int steps_taken) {
    if (current_cost > max_cost) return;
    if (steps_taken == max_steps) {
        if (current_cost >= min_cost && current_cost <= max_cost) {
            valid_endpoints.push_back(current_node);
        }
        return;
    }
    for (auto [neighbor, weight] : graph[current_node]) {
        traverse(neighbor, current_cost + weight, steps_taken + 1);
    }
}

void solve() {
    cin >> nodes >> edges >> max_steps >> min_cost >> max_cost;
    int u, v, w;
    for (int i = 1; i <= edges; i++) {
        cin >> u >> v >> w;
        graph[u].push_back({v, w});
    }
    traverse(1, 0, 0);
    sort(valid_endpoints.begin(), valid_endpoints.end());
    valid_endpoints.erase(unique(valid_endpoints.begin(), valid_endpoints.end()), valid_endpoints.end());
    for (auto endpoint : valid_endpoints) cout << endpoint << " ";
}

E - Character Sequnece Ranking

We can map this problem to a 2D plane where 'A' is +1 and 'B' is -1. For a fixed starting position, we need to count how many subsequent values are greater than our current value. This naturally suggests using a balanced binary search tree.

int length;
string sequence;
int prefix_values[MAX_LEN];
struct TreapDataStructure {
    int node_count = 0, root = 0;
    struct node {
        int left_child, right_child;
        int key, priority;
        int size;
    } tree[MAX_NODES];
    
    void create_node(int x) {
        node_count++;
        tree[node_count].size = 1;
        tree[node_count].left_child = tree[node_count].right_child = 0;
        tree[node_count].key = x;
        tree[node_count].priority = rand();
    }
    
    void update_size(int k) {
        tree[k].size = tree[tree[k].left_child].size + tree[tree[k].right_child].size + 1;
    }
    
    void split(int u, int x, int &left_tree, int &right_tree) {
        if (u == 0) {
            left_tree = right_tree = 0;
            return;
        }
        if (tree[u].key <= x) {
            left_tree = u;
            split(tree[u].right_child, x, tree[u].right_child, right_tree);
        } else {
            right_tree = u;
            split(tree[u].left_child, x, left_tree, tree[u].left_child);
        }
        update_size(u);
    }
    
    int merge(int left_tree, int right_tree) {
        if (left_tree == 0 || right_tree == 0) return left_tree + right_tree;
        if (tree[left_tree].priority > tree[right_tree].priority) {
            tree[left_tree].right_child = merge(tree[left_tree].right_child, right_tree);
            update_size(left_tree);
            return left_tree;
        } else {
            tree[right_tree].left_child = merge(left_tree, tree[right_tree].left_child);
            update_size(right_tree);
            return right_tree;
        }
    }
    
    void insert(int x) {
        int left_tree, right_tree;
        split(root, x, left_tree, right_tree);
        create_node(x);
        root = merge(merge(left_tree, node_count), right_tree);
    }
    
    void remove(int x) {
        int left_tree, right_tree, middle_tree;
        split(root, x, left_tree, right_tree);
        split(left_tree, x-1, left_tree, middle_tree);
        middle_tree = merge(tree[middle_tree].left_child, tree[middle_tree].right_child);
        root = merge(merge(left_tree, middle_tree), right_tree);
    }
    
    int get_rank(int x) {
        int left_tree, right_tree;
        split(root, x-1, left_tree, right_tree);
        int rank = tree[left_tree].size + 1;
        root = merge(left_tree, right_tree);
        return rank;
    }
    
    int find_kth(int u, int k) {
        if (k == tree[tree[u].left_child].size + 1) return u;
        if (k <= tree[tree[u].left_child].size) return find_kth(tree[u].left_child, k);
        else return find_kth(tree[u].right_child, k - tree[tree[u].left_child].size - 1);
    }
    
    int get_predecessor(int x) {
        int left_tree, right_tree;
        split(root, x-1, left_tree, right_tree);
        int pred = tree[find_kth(left_tree, tree[left_tree].size)].key;
        root = merge(left_tree, right_tree);
        return pred;
    }
    
    int get_predecessor_strict(int x) {
        int left_tree, right_tree;
        split(root, x, left_tree, right_tree);
        int pred = tree[find_kth(left_tree, tree[left_tree].size)].key;
        root = merge(left_tree, right_tree);
        return pred;
    }
    
    int get_successor(int x) {
        int left_tree, right_tree;
        split(root, x, left_tree, right_tree);
        int succ = tree[find_kth(right_tree, 1)].key;
        root = merge(left_tree, right_tree);
        return succ;
    }
    
    int get_size() {
        return tree[root].size;
    }
    
    int get_nearest(int x) {
        int pred = get_predecessor_strict(x), succ = get_successor(x);
        if (x - pred > succ - x) return succ;
        else return pred;
    }
} treap;

void solve() {
    cin >> length >> sequence;
    sequence = " " + sequence;
    for (int i = 1; i <= length; i++) {
        if (sequence[i] == 'A') prefix_values[i] = prefix_values[i-1] + 1;
        if (sequence[i] == 'B') prefix_values[i] = prefix_values[i-1] - 1;
        if (sequence[i] == 'C') prefix_values[i] = prefix_values[i-1];
        treap.insert(prefix_values[i]);
    }
    int result = 0, remaining = length;
    for (int i = 1; i <= length; i++) {
        result += remaining - treap.get_rank(prefix_values[i-1] + 1) + 1;
        treap.remove(prefix_values[i]);
        remaining--;
    }
    cout << result;
}

F - Optimal Shopping Strategy

A classic approach involves computing prefix and suffix solutions separately.
Note that there's a subtle difference between 2D and 1D knapsack problems - values from previous items need to be inherited.

int items, capacity;
int forward_dp[1005][MAX_CAPACITY], backward_dp[1005][MAX_CAPACITY];
int prices[1050], values[1050];

void solve() {
    cin >> items >> capacity;
    for (int i = 1; i <= items; i++) {
        cin >> prices[i] >> values[i];
    }
    
    // Forward DP (from first item to current item)
    for (int i = 1; i <= items; i++) {
        for (int j = capacity; j >= 0; j--) {
            forward_dp[i][j] = max(forward_dp[i][j], forward_dp[i-1][j]);
            if (j >= prices[i]) {
                forward_dp[i][j] = max(forward_dp[i-1][j-prices[i]] + values[i], forward_dp[i][j]);
            }
        }
    }
    
    // Backward DP (from current item to last item)
    for (int i = items; i >= 1; i--) {
        for (int j = capacity; j >= 0; j--) {
            backward_dp[i][j] = max(backward_dp[i][j], backward_dp[i+1][j]);
            if (j >= prices[i]) {
                backward_dp[i][j] = max(backward_dp[i+1][j-prices[i]] + values[i], backward_dp[i][j]);
            }
        }
    }
    
    int optimal_value = forward_dp[items][capacity];
    for (int i = 1; i <= items; i++) {
        int max_with_item = 0, max_without_item = 0;
        for (int j = 0; j <= capacity; j++) {
            max_without_item = max(max_without_item, forward_dp[i-1][j] + backward_dp[i+1][capacity-j]);
            if (j >= prices[i]) {
                max_with_item = max(max_with_item, values[i] + forward_dp[i-1][j-prices[i]] + backward_dp[i+1][capacity-j]);
            }
        }
        if (max_with_item == optimal_value && max_without_item < optimal_value) cout << "A";
        if (max_with_item == optimal_value && max_without_item == optimal_value) cout << "B";
        if (max_without_item == optimal_value && max_with_item < optimal_value) cout << "C";
    }
}

Tags: depth-first-search balanced-trees dynamic-programming knapsack-problem Treap

Posted on Fri, 29 May 2026 19:58:58 +0000 by volant