Problem
Problem ID: 679
Title: 24 Game
Difficulty: Hard
Description:
You are given an integer array cards of length 4. You have four cards, each containing a number in the range [1, 9]. You should arrange the numbers on these cards in a mathematical expression using the operators ['+', '-', '*', '/']
and the parentheses '(' and ')'
to get the value 24.
You are restricted with the following rules:
The division operator ‘/’ represents real division, not integer division. For example, 4 / (1 - 2 / 3) = 4 / (1 / 3) = 12. Every operation done is between two numbers. In particular, we cannot use ‘-‘ as a unary operator. For example, if cards = [1, 1, 1, 1], the expression “-1 - 1 - 1 - 1” is not allowed. You cannot concatenate numbers together For example, if cards = [1, 2, 1, 2], the expression “12 + 12” is not valid. Return true if you can get such expression that evaluates to 24, and false otherwise.
Thoughts
From a glance you can see that this is a back tracking problem but the difficulty is identify the right recurssive function.
Solution
General Idea
As any kind parentheses are allowed, each number could be left associative or right associative (ie (x+y)+z or x + (y+z)). We solve this by recurssively splitting the numbers into two halves. By doing so we will abstract away the issue of left or right associative, as there are only two operands (ie (…) + (…)) in each iteration.
Implementation
vector<double> helper(vecotor<double> cards)
- Given a list of cards returns all the possible values that can be evaluated form the list of cards
- Base case: when the size of
cards
is1
the only possible value to be evaluated is the card itself - Recursion:
- Iteratively split the current cards into two halves
- Call the recursive function on each half to find the list of possible evaluated
- For each possible value in the left and right half, iterate through all the operations to get a possible evaluated value for the current cards
- Iterate through the list of all possible evaluated values for the 4 provided digits and find if any of them equals to
24
Edge Cases:
- To prevent divide by
0
, set the divide operation to return a dummy value if RHS is 0. - Due to floating point precision, we will need to check
24 - epsilon < val < 24 + epsilon
.
Implementation
class Solution {
public:
vector<function<double(double, double)>> ops = {
[](double l, double r) {
return l + r;
},
[](double l, double r) {
return l * r;},
[](double l, double r) {
return l - r;
},
[](double l, double r) {
if (r == 0) return 1e9;
return l / r;
},
};
vector<double> helper(vector<double> cards) {
if (cards.size() == 1) return {cards.front()};
vector<double> ret;
for (auto it = cards.begin() + 1; it != cards.end(); it++) {
vector<double> l(cards.begin(), it);
vector<double> r(it, cards.end());
vector<double> l_s = helper(l);
vector<double> r_s = helper(r);
for (auto l_it = l_s.begin(); l_it != l_s.end(); l_it++) {
for (auto r_it = r_s.begin(); r_it != r_s.end(); r_it++) {
for (auto op : ops) {
auto val = op(*l_it, *r_it);
ret.push_back(val);
}
}
}
}
return ret;
}
bool judgePoint24(vector<int>& cards) {
vector<double> d_cards;
for (auto card : cards) d_cards.push_back(card);
sort(d_cards.begin(), d_cards.end());
vector<double> vals;
do {
auto curr_vals = helper(d_cards);
vals.insert(vals.end(), curr_vals.begin(), curr_vals.end());
} while(next_permutation(d_cards.begin(), d_cards.end()));
for (auto val : vals) if (val > 23.99 && val < 24.01) return true;
return false;
}
};