Cribbage On Steroids ⧉
At the time of writing, this problem has a difficulty rating of 6.7 on Kattis, which qualifies as a Hard. It's fresh from the contest — I think the difficulty will even out to around a 4.0.
This is a 3-part implementation task. Implement a cribbage scorer for n
-length hands.
There are three main subtasks: Given an array of integers...
- ... count all the ways you can sum to 15.
This is solveable inn^2
steps using dynamic programming. See the functioncount_sums_to_k
for implementation details. - ... count all pairs of identical values.
This is given byn
choose 2 (n(n-1)/2
), withn
being the frequency of each value in the array. - ... count all ways to make a maximum length contiguous runs of length 3 or more after sorting the array.
This is done in linear time by iterating over an ordered map of the{ value, frequency }
.
There are more implementation details to consider. For instance, Aces have a face value of 1, but 10s, Jacks, Queens and Kings all have a face value of 10. Keeping all the requirements in order is key.
No one task is difficult on its own, but implementing all three without writing a bug is the challenge! I overlooked the "length 3 or more" requirement for counting runs on my first try. Took a minute to catch.
_107#include <iostream>_107#include <algorithm>_107#include <numeric>_107#include <map>_107#include <functional>_107#include <vector>_107_107using ll = long long;_107_107/\*\*_107_107- Count all the ways to sum to `k` with the values in `arr`._107- Complexity: O(|arr|^2)_107- \*/_107 ll count_sums_to_k(ll k, const std::vector<ll>& arr) {_107 // `sum[i]` = the number of subsets of `arr` that sum to `i`_107 std::vector<ll> sum (k+1, 0);_107 sum[0] = 1; // We can sum to 0 by adding 0 elements together :)_107 for (ll x : arr) {_107 for (int i = k; i >= 0; --i) {_107 if (sum[i] != 0 && i+x <= k) {_107 // Since `sum[i]` subsets already sum to `i`,_107 // we can add `x` to each of those subsets,_107 // summing to `i+x` in `sum[i]` different ways!_107 sum[i+x] += sum[i];_107 }_107 }_107 }_107 return sum[k];_107 }_107_107/\*\*_107_107- Count all disjoint 'runs' of length 3 or more._107- _/_107 ll count_runs(const std::map<ll, ll>& counts) {_107 ll score = 0;_107 ll len = 1;_107 ll card = -1;_107 ll mul = 1;_107 for (const auto& [v, k] : counts) {_107 if (card == -1) { card = v; mul = k; continue; }_107 if (v == card+1) {_107 mul _= k;_107 len++;_107 } else {_107 score += (len > 2 ? mul _ len : 0LL);_107 len = 1;_107 mul = k;_107 }_107 card = v;_107 }_107 return score + (len > 2 ? mul _ len : 0LL);_107 }_107_107/\*\*_107_107- AKA count the pairs that can be made from `n` identical elements. \**/_107 ll n_choose_2(ll n) {_107 return (n*n - n)/2;_107 }_107_107int main () {_107// map each card to a number that reflects the order of the ranks_107const auto encode = [] (char card) -> ll {_107if (card == 'A') return 1LL;_107if (card == 'T') return 10LL;_107if (card == 'J') return 11LL;_107if (card == 'Q') return 12LL;_107if (card == 'K') return 13LL;_107return card - '0';_107};_107_107 int n;_107 std::cin >> n;_107 std::vector<char> cards (n);_107 std::map<ll, ll> counts;_107_107 for (auto& card : cards) {_107 std::cin >> card;_107 counts[encode(card)]++;_107 }_107_107 // Ace through 9 map to 1 through 9. 10s, Jacks, Queens and Kings are worth 10._107 const auto face_value = [] (ll encoding) -> ll {_107 if (encoding >= 1 && encoding <= 9) return encoding;_107 return 10LL;_107 };_107_107 std::vector<ll> values (n);_107 std::transform(cards.begin(), cards.end(), values.begin(),_107 [&] (char card) {_107 return face_value(encode(card));_107 });_107_107 ll to15 = count_sums_to_k(15, values);_107_107 ll pairs = std::accumulate(counts.begin(), counts.end(), 0LL,_107 [] (ll acc, const auto& p) {_107 return acc + n_choose_2(p.second);_107 });_107_107 ll runs = count_runs(counts);_107_107 std::cout << 2 * (to15 + pairs) + runs << '\n';_107_107}
_1
If you found this solution helpful, consider leaving a star!