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...

  1. ... count all the ways you can sum to 15.
    This is solveable in n^2 steps using dynamic programming. See the function count_sums_to_k for implementation details.
  2. ... count all pairs of identical values.
    This is given by n choose 2 (n(n-1)/2), with n being the frequency of each value in the array.
  3. ... 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
_107
using 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
_107
int main () {
_107
// map each card to a number that reflects the order of the ranks
_107
const auto encode = [] (char card) -> ll {
_107
if (card == 'A') return 1LL;
_107
if (card == 'T') return 10LL;
_107
if (card == 'J') return 11LL;
_107
if (card == 'Q') return 12LL;
_107
if (card == 'K') return 13LL;
_107
return 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!