---
# System prepended metadata

title: ACSI NOI Workshop 2021 Solution

---

# ACSI NOI Workshop 2021 Solution

## Lesson Problems

### Problem 1: Quality-Adjusted Life-Year

- https://open.kattis.com/problems/qaly

```cpp=1
#include <bits/stdc++.h>
using namespace std;

// Declare variables here
int N;
float q, y, answer;

int main() {
    // Read integer N
    cin >> N;

    // Repeat N times:
    for (int i = 0; i < N; i++) {
        //  1. Read float q, y
        cin >> q >> y;
        //  2. Add q * y to answer
        answer += q * y;
    }

    // Output answer
    cout << answer;
}
```

### Problem 2: Poker Hand

- https://open.kattis.com/problems/pokerhand

```cpp=1
#include <bits/stdc++.h>
using namespace std;

int ans;
string S[5];
// Declare variables here
unordered_map<char, int> freq;

int main() {
    // Read 5 strings into array S[]
    for (int i = 0; i < 5; i++) {
        cin >> S[i];
    }

    // For each hand in S[]:
    for (int i = 0; i < 5; i++) {
        // 1. Get the rank
        char rank = S[i][0];
        // 2. Increase the rank's count by 1
        freq[rank]++;
    }

    // For each rank's count:
    for (auto x : freq) {
        // 1. Update answer if count > current answer
        ans = max(ans, x.second);
    }

    // Output answer
    cout << ans;
}
```

### Problem 3: Bungee Builder

- https://open.kattis.com/problems/bungeebuilder

```cpp=1
#include <bits/stdc++.h>
using namespace std;

#define MAXN 1000006

// Declare variables here
int N, height[MAXN];

// Sliding window from left to right, returns the max distance, O(N)
int sliding(int N, int height[]) {
    int i = 0, ans = 0, bottom = height[0];

    for (int j = 1; j < N; j++) {
        if (height[i] <= height[j]) {
            // Form a bridge between i and j, calculate the max distance
            ans = max(ans, height[i] - bottom);
            // Set j as the new i
            i = j;
            bottom = height[i];
        } else {
            // Keep searching for the lowest bottom
            bottom = min(bottom, height[j]);
        }
    }

    return ans;
}

int main() {
    // Read inputs
    cin >> N;
    for (int i = 0; i < N; i++) {
        cin >> height[i];
    }

    // Get forward result
    int ans_forward = sliding(N, height);
    // Reverse the height array in-place
    reverse(begin(height), begin(height) + N);
    // Get backward result
    int ans_backward = sliding(N, height);

    // Output the max of both forward & backward result
    cout << max(ans_forward, ans_backward);
}
```

### Problem 4: The Monkey and the Oiled Bamboo

- https://onlinejudge.org/external/120/12032.pdf

```cpp=1
#include <bits/stdc++.h>
using namespace std;

// Bad practice in software engineering, but ok for CP
#define MAXN 100005
#define MAXR 10000007

// Declare variables here
int T, N, R[MAXN], ans;

// Returns true if can reach the top rung with k
bool can_reach(int k, int R[]) {
    for (int i = 0; i < N; i++) {
        int gap = (i == 0) ? R[0] : R[i] - R[i - 1];

        if (gap > k) {
            return false;
        } else if (gap == k) {
            k--;
        }
    }
    return true;
}

int main() {
    // Read T
    cin >> T;
    // For each case i
    for (int i = 0; i < T; i++) {
        // Read input
        cin >> N;
        for (int j = 0; j < N; j++) {
            cin >> R[j];
        }

        // Initialize binary search low & high
        int lo = 1, hi = MAXR;
        // Binary search terminating condition
        while (lo <= hi) {
            // Pick the middle as k
            int mid = hi - (hi - lo) / 2;  // Prevent integer overflow

            if (can_reach(mid, R)) {
                // Keep the answer
                ans = mid;
                // Look for smaller k
                hi = mid - 1;
            } else {
                // Look for bigger k
                lo = mid + 1;
            }
        }

        // Output answer
        printf("Case %d: %d\n", i + 1, ans);
    }
}
```

### Problem 5: Station Balance

- https://onlinejudge.org/external/4/410.pdf

```cpp=1
#include <bits/stdc++.h>
using namespace std;

#define MAXS 10

// Declare variables here
int C, S, M[MAXS], TC = 1, s1, s2;
double imb, sum, avg;

int main() {
    while (cin >> C >> S) {
        sum = imb = 0;
        // Read input
        for (int i = 0; i < S; i++) {
            cin >> M[i];
            sum += M[i];
        }
        avg = sum / C;

        // Pad M with 0
        for (int i = S; i < 2 * C; i++) {
            M[i] = 0;
        }

        sort(M, M + 2 * C);

        // Output answer
        printf("Set #%d\n", TC++);
        for (int i = 0; i < C; i++) {
            printf(" %d:", i);
            s1 = M[i], s2 = M[2 * C - i - 1];
            // Don't print 0
            if (s1) printf(" %d", s1);
            if (s2) printf(" %d", s2);
            printf("\n");
            imb += abs(avg - (s1 + s2));
        }

        // 5 digits after decimal point
        printf("IMBALANCE = %.5f\n\n", imb);
    }
}
```

### Problem 6: Wedding Shopping

- https://onlinejudge.org/external/114/11450.pdf

```cpp=1
#include <bits/stdc++.h>
using namespace std;

#define INF 1000000009
#define MAXC 20
#define MAXK 20
#define MAXM 200

int N, M, C, K, price[MAXC][MAXK], memo[MAXK][MAXM];

// g is garment type, b is current budget
int buy(int g, int b) {
    if (b < 0) return -INF;
    if (g == C) return M - b;
    if (memo[g][b] != -1) return memo[g][b];

    // Try each model k for garment g
    int ans = -1;
    for (int k = 1; k <= price[g][0]; k++)
        ans = max(ans, buy(g + 1, b - price[g][k]));
    return memo[g][b] = ans;
}

int main() {
    scanf("%d", &N);
    while (N--) {
        // Read input
        scanf("%d %d", &M, &C);
        for (int i = 0; i < C; i++) {
            // price[i][0] is number of models for garment i
            scanf("%d", &price[i][0]);
            for (int j = 1; j <= price[i][0]; j++)
                // price[i][j] is the price of model j for garment i
                scanf("%d", &price[i][j]);
        }

        // Clear memory
        memset(memo, -1, sizeof memo);

        int ans = buy(0, M);

        // Output answer
        if (ans >= 0)
            printf("%d\n", ans);
        else
            printf("no solution\n");
    }
}
```

## Day 1 Homework

### Delimiter Soup

- https://open.kattis.com/problems/delimitersoup

```cpp=1
#include <bits/stdc++.h>
using namespace std;

int L;
string str;
stack<char> expect;
unordered_map<char, char> match = {{'(', ')'}, {'[', ']'}, {'{', '}'}};

int main() {
    // Read input, cin >> ws to consume endline token for getline to work
    cin >> L >> ws;
    getline(cin, str);

    for (int i = 0; i < L; i++) {
        char c = str[i];
        // Ignore whitespace
        if (isspace(c)) continue;

        auto delim = match.find(c);
        if (delim != match.end()) {
            // c is open delimiter
            expect.push(delim->second);  // Store the expected close delimiter in a stack
        } else {
            // c is close delimiter
            if (!expect.empty() && c == expect.top())
                expect.pop();
            else {
                // c is not the expected close delimiter
                cout << c << " " << i;
                // Terminate program early
                return 0;
            }
        }
    }
    cout << "ok so far";
}
```

### Teque

- https://open.kattis.com/problems/teque

```cpp=1
#include <bits/stdc++.h>
using namespace std;

int N, x;
string op;
deque<int> d1, d2;
vector<int> ans;

int main() {
    // Fast I/O
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    cin >> N;
    for (int i = 0; i < N; i++) {
        cin >> op >> x;

        // O(1) time for every operation type
        if (op == "get")
            ans.push_back(x < d1.size() ? d1[x] : d2[x - d1.size()]);
        else if (op == "push_back")
            d2.push_back(x);
        else if (op == "push_front")
            d1.push_front(x);
        else if (op == "push_middle")
            // Insert at the front of d2
            d2.push_front(x);

        // Rebalance d1 and d2
        // d1 is always the same size or 1 item more than d2
        if (d1.size() < d2.size()) {
            d1.push_back(d2.front());
            d2.pop_front();
        } else if (d1.size() > d2.size() + 1) {
            d2.push_front(d1.back());
            d1.pop_back();
        }
    }

    // Output all answer at one go for speed
    for (int x : ans)
        cout << x << "\n";
}
```

### Pivot

- https://open.kattis.com/problems/pivot

```cpp=1
#include <bits/stdc++.h>
using namespace std;

#define MAXN 100005

int N, a[MAXN], max_left[MAXN], min_right[MAXN];

int main() {
	// Fast I/O
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    // Read input
    cin >> N;
    for (int i = 0; i < N; i++)
        cin >> a[i];

    // min_left[i] is the minimum value from everything to the left of a[i]
    max_left[0] = a[0];
    for (int i = 1; i < N; i++)
        max_left[i] = max(max_left[i - 1], a[i]);

    // max_right[i] is the maximum value from everything to the left of a[i]
    min_right[N - 1] = a[N - 1];
    for (int i = N - 2; i >= 0; i--)
        min_right[i] = min(min_right[i + 1], a[i]);

    int ans = 0;
    for (int i = 0; i < N; i++)
        // Count number of max_left[i] == min_right[i]
        ans += (max_left[i] == min_right[i]);

    // Output answer
    cout << ans;
}
```

### Disastrous Downtime

- https://open.kattis.com/problems/downtime

```cpp=1
#include <bits/stdc++.h>
using namespace std;

#define MAXN 100005

int n, k, request, ans, t[MAXN];
queue<int> process;

int main() {
    // Fast I/O
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    // Read input
    cin >> n >> k;
    for (int i = 0; i < n; i++) {
        cin >> t[i];
        // Add time for request to finish processed
        process.push(t[i] + 1000);
        // Add request count by 1
        request++;

        // Remove all processed requests before t[i]
        while (!process.empty() && process.front() <= t[i]) {
            process.pop();
            request--;
        }

        // Update answer
        ans = max(ans, request);
    }

    // Note the float conversion for ceil to work
    cout << ceil((float)ans / k) << "\n";
}
```

## Day 2 Homework

### Knigs of the Forest

- https://open.kattis.com/problems/knigsoftheforest

```cpp=1
#include <bits/stdc++.h>
using namespace std;

#define MAXN 100005

int k, n, y, p, karl, winner, power[MAXN];
priority_queue<int> pool;

int main() {
    // Fast I/O
    ios_base::sync_with_stdio(0);
    cin.tie(NULL);

    // Read input
    cin >> k >> n;
    for (int i = 0; i <= n + k - 2; i++) {
        cin >> y >> p;
        // Store Karl's power
        if (i == 0) karl = p;
        // If year is 2011, it is an initial moose
        if (y == 2011) pool.push(p);
        // Otherwise, it is a replacement moose
        else power[y - 2011] = p;
    }

    // Simulate from index 0 to index n - 1
    for (int i = 0; i < n; i++) {
        winner = pool.top();
        // Karl wins, early terminate
        if (winner == karl) {
            cout << i + 2011 << "\n";
            return 0;
        } else {
            // Remove winner
            pool.pop();
            // Insert next year's replacement
            pool.push(power[i + 1]);
        }
    }
    // Karl never wins, output unknown
    cout << "unknown\n";
}
```

### Candy Division

- https://open.kattis.com/problems/candydivision

```cpp=1
#include <bits/stdc++.h>
using namespace std;

// Using 'll' as the shorthand of 'long long'
typedef long long ll;

// Note that we need to use long long instead of integers due to constraints (10^12)
ll n, sqrt_n;
// Using set because it automatically sorts items and removes duplicates
set<ll> ans;

int main() {
    // Fast I/O
    ios_base::sync_with_stdio(0);
    cin.tie(NULL);

    // Read input
    cin >> n;

    sqrt_n = sqrt(n);
    // Find divisors from 1 to square root of n
    for (ll i = 1; i <= sqrt_n; i++) {
        // If n mod i = 0, i.e. n is divisible by i
        if (n % i == 0) {
            // Problem requires (divisors - 1)
            ans.insert(i - 1);
            ans.insert((n / i) - 1);
        }
    }

    // Output answer
    for (auto it = ans.begin(); it != ans.end(); it++)
        cout << *it << " ";
    cout << "\n";
}
```

### Doctor Kattis

- https://open.kattis.com/problems/doctorkattis

```cpp=1
#include <bits/stdc++.h>
using namespace std;

// A Cat is a pair of <level, time>
typedef pair<int, int> Cat;

#define fi first
#define se second

int n, op, level, inc;
string name;

struct CatComparator {
    bool operator()(const Cat& cat1, const Cat& cat2) const {
        // Sort Cats by level descending, then by time ascending
        return cat1.fi != cat2.fi ? cat1.fi > cat2.fi : cat1.se < cat2.se;
    }
};

map<Cat, string, CatComparator> cat_to_name;  // Cat -> name
unordered_map<string, Cat> name_to_cat;       // name -> Cat

int main() {
    // Fast I/O
    ios_base::sync_with_stdio(0);
    cin.tie(NULL);

    // Read input
    cin >> n;
    for (int time = 0; time < n; time++) {
        cin >> op;
        if (op == 0) {
            // Arrive at clinic
            cin >> name >> level;

            // Create a new entry in both containers
            Cat cat = {level, time};  // O(1)
            cat_to_name[cat] = name;  // O(logn)
            name_to_cat[name] = cat;  // O(1)
        } else if (op == 1) {
            // Update infection level
            cin >> name >> inc;

            // Use unordered_map to retrieve Cat by name
            Cat& cat = name_to_cat[name];  // O(1)
            // Remove the Cat entry from map
            cat_to_name.erase(cat);  // O(logn)
            // Increment infection level
            // At line 45, Cat is a reference to name_to_cat's value,
            // thus name_to_cat's value is updated by line 51
            cat.fi += inc;
            // Insert the updated cat into map
            // We have to delete the old Cat->name entry from cat_to_name and
            // insert a new one because Cat is a key in cat_to_name, not a value.
            cat_to_name[cat] = name;  // O(logn)
        } else if (op == 2) {
            // Leave clinic
            cin >> name;

            Cat& cat = name_to_cat[name];  // O(1)
            // Remove Cat entry from both containers
            cat_to_name.erase(cat);   // O(logn)
            name_to_cat.erase(name);  // O(1), not necessary
        } else {
            // Output highest infection cat
            if (cat_to_name.empty())
                cout << "The clinic is empty\n";
            else
                cout << cat_to_name.begin()->se << "\n";  // O(1)
        }
    }
}
```

### CD

- https://open.kattis.com/problems/cd

```cpp=1
#include <bits/stdc++.h>
using namespace std;

int n, m, ans, x;
unordered_set<int> jack;

int main() {
    // Fast I/O
    ios_base::sync_with_stdio(0);
    cin.tie(NULL);

    while (cin >> n >> m) {
        // Keep looping until input is 0 0
        if (n == 0 && m == 0) break;

        // Reset counters
        ans = 0;
        jack.clear();

        // Read input
        while (n--) {
            cin >> x;
            jack.insert(x);
        }
        while (m--) {
            cin >> x;
            if (jack.find(x) != jack.end()) ans++;
        }

        // Output answer
        cout << ans << "\n";
    }
}
```

### Recount

- https://open.kattis.com/problems/recount

```cpp=1
#include <bits/stdc++.h>
using namespace std;

#define fi first
#define se second

int most_votes, winner_count;
string name, winner;
unordered_map<string, int> candidates;

int main() {
    // Fast I/O
    ios_base::sync_with_stdio(0);
    cin.tie(NULL);

    // Read input
    while (getline(cin, name)) {
        if (name == "***") break;

        // Create new candidate entry or increase candidate's count by 1
        if (candidates.find(name) == candidates.end())
            candidates[name] = 1;
        else
            candidates[name]++;
    }

    // Find most votes
    for (auto it = candidates.begin(); it != candidates.end(); it++)
        most_votes = max(most_votes, it->se);

    // Count candidates with most_votes, get winner's name
    for (auto it = candidates.begin(); it != candidates.end(); it++) {
        if (it->se == most_votes) {
            winner = it->fi;
            winner_count++;
        }
    }

    // Output answer
    if (winner_count > 1)
        cout << "Runoff!" << endl;
    else
        cout << winner << endl;
}
```

### Conversation Log

- https://open.kattis.com/problems/conversationlog

```cpp=1
#include <bits/stdc++.h>
using namespace std;

#define fi first
#define se second

int m;
string line, name;
set<string> names;
unordered_map<string, int> word_count;
unordered_map<string, set<string>> word_to_names;
vector<string> msg;
vector<pair<int, string>> common_words;

int main() {
    // Fast I/O
    ios_base::sync_with_stdio(0);
    cin.tie(NULL);

    // Read input and a whitespace for getline to work
    cin >> m >> ws;
    for (int i = 0; i < m; i++) {
        // Read the whole input line
        getline(cin, line);

        // Clear previous msg first
        msg.clear();
        // Split whole line by whitespace, store into msg
        size_t pos = 0, next_pos;
        while ((next_pos = line.find(" ", pos)) != string::npos) {
            msg.push_back(line.substr(pos, next_pos - pos));
            pos = next_pos + 1;
        }
        msg.push_back(line.substr(pos, next_pos - pos));

        name = msg[0];
        // Keep track of a set of unique names
        names.insert(name);

        // For each word in the msg
        for (auto it = msg.begin() + 1; it != msg.end(); it++) {
            string word = *it;

            // Keep track of people who have used the word
            word_to_names[word].insert(name);

            // Update the word's count
            if (word_count.find(word) == word_count.end())
                word_count[word] = 1;
            else
                word_count[word]++;
        }
    }

    // Find common words that are mentioned by every user
    for (auto it = word_to_names.begin(); it != word_to_names.end(); it++) {
        string word = it->fi;
        int mentions = it->se.size();

        if (mentions == names.size()) {
            // Negate first value for descending order
            // Alternatively, you can define your own comparator
            common_words.push_back(make_pair(-word_count[word], word));
        }
    }

    // Sort common words
    sort(common_words.begin(), common_words.end());

    // Output answer
    if (common_words.empty())
        cout << "ALL CLEAR\n";
    else
        for (auto x : common_words) cout << x.se << "\n";
}
```

## Day 3 Homework

### The Easiest Problem Is This One

- https://open.kattis.com/problems/easiest

```cpp=1
#include <bits/stdc++.h>
using namespace std;

int n, target, ans;

// Find the sum of digit, O(# digits) i.e. O(logn)
int sum_digit(int x) {
    int result = 0;
    while (x) {
        result += x % 10;
        x /= 10;
    }
    return result;
}

int main() {
    // Fast I/O
    ios_base::sync_with_stdio(0);
    cin.tie(NULL);

    while (cin >> n) {
        // Terminate program when input is 0
        if (n == 0) break;

        target = sum_digit(n);

        ans = 10;
        // Keep trying new answer by increasing 1
        while (ans++) {
            if (sum_digit(n * ans) == target) {
                // Found correct answer
                cout << ans << "\n";
                break;
            }
        }
    }
}
```

### Watering Grass

- https://open.kattis.com/problems/grass

```cpp=1
#include <bits/stdc++.h>
using namespace std;

#define EPS 0.000000009

// A sprinkler has a leftmost & rightmost fully covered points
struct sp {
    double l, r;
};

bool cmp(sp a, sp b) {
    return fabs(a.l - b.l) > EPS ? a.l < b.l : a.r > b.r;
}

int n, length, width, center, radius;
vector<sp> sprinkler;

int main() {
    // Fast I/O
    ios_base::sync_with_stdio(0);
    cin.tie(NULL);

    while (cin >> n >> length >> width) {
        // Read input
        for (int i = 0; i < n; i++) {
            cin >> center >> radius;

            // Discard sprinklers that cannot fully cover the width
            if (2 * radius < width) continue;

            // Find the inner square of the circle using Pythagoras Theorem
            double dx = sqrt(pow(radius, 2) - pow((width / 2.0), 2));
            // Leftmost & rightmost fully covered points of a sprinkler
            sprinkler.push_back(sp{center - dx, center + dx});
        }

        // Sort sprinklers with ascending l, then descending r
        sort(sprinkler.begin(), sprinkler.end(), cmp);

        // Area covered (from left) so far
        double covered = 0.0;
        int ans = 0;
        bool possible = true;
        // Checking each sprinkler
        for (int i = 0; i < sprinkler.size() && possible; i++) {
            // Covered the whole grass strip
            if (covered >= length) break;
            // Discard sprinkler if its completely inside the covered area
            else if (sprinkler[i].r < covered)
                continue;
            // There is a gap that is impossible to cover with any sprinkler
            else if (sprinkler[i].l > covered)
                possible = false;
            // Given the currently "covered" point, look for sprinklers that overlaps "covered"
            // and has the largest coverage
            else {
                // Initialize the current sprinkler as the best sprinkler
                double max_right = sprinkler[i].r;
                int max_i = i;
                // Check all sprinklers that l <= covered
                while (++i < sprinkler.size() && sprinkler[i].l <= covered)
                    // Found a sprinkler that has larger coverage
                    if (sprinkler[i].r > max_right) {
                        max_right = sprinkler[i].r;
                        // Keep the max coverage sprinkler's index
                        max_i = i;
                    }
                // Set the area covered so far as the right point of the selected sprinkler
                covered = max_right;
                i = max_i;
                ans++;
            }
        }

        // Output answer
        if (!possible || covered < length)
            cout << "-1\n";
        else
            cout << ans << "\n";

        // Clear container
        sprinkler.clear();
    }
}
```

### The Dragon of Loowater

- https://open.kattis.com/problems/loowater

```cpp=1
#include <bits/stdc++.h>
using namespace std;

#define MAXN 20004

int n, m, dragon[MAXN], knight[MAXN];

int main() {
    // Fast I/O
    ios_base::sync_with_stdio(0);
    cin.tie(NULL);

    // Read input
    while (cin >> n >> m) {
        // Terminate program when input is 0 0
        if (n == 0 && m == 0) break;

        for (int i = 0; i < n; i++) cin >> dragon[i];
        for (int i = 0; i < m; i++) cin >> knight[i];

        // Sort both arrays
        sort(dragon, dragon + n);
        sort(knight, knight + m);

        // Assign each dragon to the smallest possible knight
        int k = 0, ans = 0;
        bool doomed = false;
        for (int d = 0; d < n && !doomed; d++) {
            // Finding a knight
            while (k < m && knight[k] < dragon[d]) k++;
            // Ran out of knight
            if (k == m) doomed = true;
            // Found a knight
            ans += knight[k++];
        }

        if (doomed)
            cout << "Loowater is doomed!\n";
        else
            cout << ans << "\n";
    }
}
```

### Traveling Monk

- https://open.kattis.com/problems/monk

```cpp=1
#include <bits/stdc++.h>
using namespace std;

#define MAXN 5003
#define MAXT 5000005
#define EPS 0.000001

// A segment has a height, time and cumulative time
struct Segment {
    int h, t, ct;
} asc[MAXN], dsc[MAXN];

// Get the height at a specific time, O(n)
double get_height(const Segment sg[], int n, double time) {
    // Cumulative height from previous segments
    double cum_height = 0.0;

    // Find the time's current segment
    Segment cur;
    for (int i = 0; i < n; i++) {
        cur = sg[i];
        if (time <= sg[i].ct) break;
        cum_height += sg[i].h;
    }

    // Relative time within the segment
    double rel_time = cur.t - (cur.ct - time);
    // Relative height within the segment
    double rel_height = (double)cur.h / cur.t * rel_time;

    // Real height at the given time
    return cum_height + rel_height;
}

int a, d, total_h;

int main() {
    // Read input
    cin >> a >> d;
    for (int i = 0; i < a; i++) {
        cin >> asc[i].h >> asc[i].t;
        asc[i].ct = (i == 0) ? asc[0].t : asc[i - 1].ct + asc[i].t;
        total_h += asc[i].h;
    }
    for (int i = 0; i < d; i++) {
        cin >> dsc[i].h >> dsc[i].t;
        dsc[i].ct = (i == 0) ? dsc[0].t : dsc[i - 1].ct + dsc[i].t;
    }

    // BSTA
    double lo = 0, hi = MAXT;
    while (fabs(hi - lo) > EPS) {
        double mid = (lo + hi) / 2.0;
        double asc_height = get_height(asc, a, mid);
        double dsc_height = total_h - get_height(dsc, d, mid);

        (asc_height < dsc_height) ? lo = mid : hi = mid;
    }

    // Output answer
    printf("%.6f\n", hi);
}
```

### Knapsack

- https://open.kattis.com/problems/knapsack

```cpp=1
#include <bits/stdc++.h>
using namespace std;

typedef vector<int> vi;
typedef vector<vi> vvi;

const int MAXN = 2003;

int C, n, value[MAXN], weight[MAXN];
vvi memo;

// Find the best value after considering item i with w allowed weight
int top_down(int i, int w) {
    // Previously computed state
    if (memo[i][w] != -1) return memo[i][w];

    // Base case: 0 item or no remaining weight
    if (i == 0 || w == 0) return memo[i][w] = 0;

    // If item is heavier than the bag's allowed weight, don't take it
    if (w < weight[i]) return memo[i][w] = top_down(i - 1, w);
    // Otherwise consider either taking or not taking the item
    return memo[i][w] = max(top_down(i - 1, w - weight[i]) + value[i],
                            top_down(i - 1, w));
}

// Fill the memo table using the bottom-up fashion
void bottom_up() {
    for (int i = 0; i <= n; i++) {
        for (int w = 0; w <= C; w++) {
            if (i == 0 || w == 0)
                memo[i][w] = 0;
            else if (w < weight[i])
                memo[i][w] = memo[i - 1][w];
            else
                memo[i][w] = max(memo[i - 1][w - weight[i]] + value[i],
                                 memo[i - 1][w]);
        }
    }
}

// Backtrack the memo to trace which items are selected
void trace(int i, int w, vi& bag) {
    // Traced all items, job done
    if (i == 0) return;

    if (memo[i][w] == memo[i - 1][w]) {
        // Not taking item i
        return trace(i - 1, w, bag);
    } else {
        // Taking item i
        bag.push_back(i);
        return trace(i - 1, w - weight[i], bag);
    }
}

int main() {
    while (cin >> C >> n) {
        // Fast I/O
        ios_base::sync_with_stdio(0);
        cin.tie(NULL);

        // Read input
        for (int i = 1; i <= n; i++)  // Change to 1-indexed
            cin >> value[i] >> weight[i];

        // Reset memo
        memo = vector<vi>(n + 1, vi(C + 1, -1));
        vi bag;

        // Dynamic programming
        // You may experiment by commenting away either 1 of the following 2 lines
        top_down(n, C);
        // bottom_up();
        trace(n, C, bag);

        // Output answer
        cout << bag.size() << "\n";
        for (int x : bag) cout << x - 1 << " ";  // Revert to 0-indexed
        cout << "\n";
    }
}
```
