I had an interview were I was asked a seemingly simple algorithm question: "Write an algorithm to return me all possible winning combinations for tic tac toe." I still can't figure out an efficient way to handle this. Is there a standard algorithm or common that should be applied to similar questions like this that I'm not aware of?
This is one of those problems that's actually simple enough for brute force and, while you could use combinatorics, graph theory, or many other complex tools to solve it, I'd actually be impressed by applicants that recognise the fact there's an easier way (at least for this problem).
There are only 39, or 19,683 possible combinations of placing x, o or <blank> in the grid, and not all of those are valid.
First, a valid game position is one where the difference between x and o counts is no more than one, since they have to alternate moves.
In addition, it's impossible to have a state where both sides have three in a row, so they can be discounted as well. If both have three in a row, then one of them would have won in the previous move.
There's actually another limitation in that it's impossible for one side to have won in two different ways without a common cell (again, they would have won in a previous move), meaning that:
XXX
OOO
XXX
cannot be achieved, while:
XXX
OOX
OOX
can be. But we can actually ignore that since there's no way to win two ways without a common cell without having already violated the "maximum difference of one" rule, since you need six cells for that, with the opponent only having three.
So I would simply use brute force and, for each position where the difference is zero or one between the counts, check the eight winning possibilities for both sides. Assuming only one of them has a win, that's a legal, winning game.
Below is a proof of concept in Python, but first the output of time when run on the process sending output to /dev/null to show how fast it is:
real 0m0.169s
user 0m0.109s
sys 0m0.030s
The code:
def won(c, n):
if c[0] == n and c[1] == n and c[2] == n: return 1
if c[3] == n and c[4] == n and c[5] == n: return 1
if c[6] == n and c[7] == n and c[8] == n: return 1
if c[0] == n and c[3] == n and c[6] == n: return 1
if c[1] == n and c[4] == n and c[7] == n: return 1
if c[2] == n and c[5] == n and c[8] == n: return 1
if c[0] == n and c[4] == n and c[8] == n: return 1
if c[2] == n and c[4] == n and c[6] == n: return 1
return 0
pc = [' ', 'x', 'o']
c = [0] * 9
for c[0] in range (3):
for c[1] in range (3):
for c[2] in range (3):
for c[3] in range (3):
for c[4] in range (3):
for c[5] in range (3):
for c[6] in range (3):
for c[7] in range (3):
for c[8] in range (3):
countx = sum([1 for x in c if x == 1])
county = sum([1 for x in c if x == 2])
if abs(countx-county) < 2:
if won(c,1) + won(c,2) == 1:
print " %s | %s | %s" % (pc[c[0]],pc[c[1]],pc[c[2]])
print "---+---+---"
print " %s | %s | %s" % (pc[c[3]],pc[c[4]],pc[c[5]])
print "---+---+---"
print " %s | %s | %s" % (pc[c[6]],pc[c[7]],pc[c[8]])
print
As one commenter has pointed out, there is one more restriction. The winner for a given board cannot have less cells than the loser since that means the loser just moved, despite the fact the winner had already won on the last move.
I won't change the code to take that into account but it would be a simple matter of checking who has the most cells (the last person that moved) and ensuring the winning line belonged to them.
Another way could be to start with each of the eight winning positions,
xxx ---
--- xxx
--- --- ... etc.,
and recursively fill in all legal combinations (start with inserting 2 o's, then add an x for each o ; avoid o winning positions):
xxx xxx xxx
oo- oox oox
--- o-- oox ... etc.,
Today I had an interview with Apple and I had the same question. I couldn't think well at that moment. Later one on, before going to a meeting I wrote the function for the combinations in 15 minutes, and when I came back from the meeting I wrote the validation function again in 15 minutes. I get nervous at interviews, Apple not trusts my resume, they only trust what they see in the interview, I don't blame them, many companies are the same, I just say that something in this hiring process doesn't look quite smart.
Anyways, here is my solution in Swift 4, there are 8 lines of code for the combinations function and 17 lines of code to check a valid board.
Cheers!!!
// Not used yet: 0
// Used with x : 1
// Used with 0 : 2
// 8 lines code to get the next combination
func increment ( _ list: inout [Int], _ base: Int ) -> Bool {
for digit in 0..<list.count {
list[digit] += 1
if list[digit] < base { return true }
list[digit] = 0
}
return false
}
let incrementTicTacToe = { increment(&$0, 3) }
let win0_ = [0,1,2] // [1,1,1,0,0,0,0,0,0]
let win1_ = [3,4,5] // [0,0,0,1,1,1,0,0,0]
let win2_ = [6,7,8] // [0,0,0,0,0,0,1,1,1]
let win_0 = [0,3,6] // [1,0,0,1,0,0,1,0,0]
let win_1 = [1,4,7] // [0,1,0,0,1,0,0,1,0]
let win_2 = [2,5,8] // [0,0,1,0,0,1,0,0,1]
let win00 = [0,4,8] // [1,0,0,0,1,0,0,0,1]
let win11 = [2,4,6] // [0,0,1,0,1,0,1,0,0]
let winList = [ win0_, win1_, win2_, win_0, win_1, win_2, win00, win11]
// 16 lines to check a valid board, wihtout countin lines of comment.
func winCombination (_ tictactoe: [Int]) -> Bool {
var count = 0
for win in winList {
if tictactoe[win[0]] == tictactoe[win[1]],
tictactoe[win[1]] == tictactoe[win[2]],
tictactoe[win[2]] != 0 {
// If the combination exist increment count by 1.
count += 1
}
if count == 2 {
return false
}
}
var indexes = Array(repeating:0, count:3)
for num in tictactoe { indexes[num] += 1 }
// '0' and 'X' must be used the same times or with a diference of one.
// Must one and only one valid combination
return abs(indexes[1] - indexes[2]) <= 1 && count == 1
}
// Test
var listToIncrement = Array(repeating:0, count:9)
var combinationsCount = 1
var winCount = 0
while incrementTicTacToe(&listToIncrement) {
if winCombination(listToIncrement) == true {
winCount += 1
}
combinationsCount += 1
}
print("There is \(combinationsCount) combinations including possible and impossible ones.")
print("There is \(winCount) combinations for wining positions.")
/*
There are 19683 combinations including possible and impossible ones.
There are 2032 combinations for winning positions.
*/
listToIncrement = Array(repeating:0, count:9)
var listOfIncremented = ""
for _ in 0..<1000 { // Win combinations for the first 1000 combinations
_ = incrementTicTacToe(&listToIncrement)
if winCombination(listToIncrement) == true {
listOfIncremented += ", \(listToIncrement)"
}
}
print("List of combinations: \(listOfIncremented)")
/*
List of combinations: , [2, 2, 2, 1, 1, 0, 0, 0, 0], [1, 1, 1, 2, 2, 0, 0, 0, 0],
[2, 2, 2, 1, 0, 1, 0, 0, 0], [2, 2, 2, 0, 1, 1, 0, 0, 0], [2, 2, 0, 1, 1, 1, 0, 0, 0],
[2, 0, 2, 1, 1, 1, 0, 0, 0], [0, 2, 2, 1, 1, 1, 0, 0, 0], [1, 1, 1, 2, 0, 2, 0, 0, 0],
[1, 1, 1, 0, 2, 2, 0, 0, 0], [1, 1, 0, 2, 2, 2, 0, 0, 0], [1, 0, 1, 2, 2, 2, 0, 0, 0],
[0, 1, 1, 2, 2, 2, 0, 0, 0], [1, 2, 2, 1, 0, 0, 1, 0, 0], [2, 2, 2, 1, 0, 0, 1, 0, 0],
[2, 2, 1, 0, 1, 0, 1, 0, 0], [2, 2, 2, 0, 1, 0, 1, 0, 0], [2, 2, 2, 1, 1, 0, 1, 0, 0],
[2, 0, 1, 2, 1, 0, 1, 0, 0], [0, 2, 1, 2, 1, 0, 1, 0, 0], [2, 2, 1, 2, 1, 0, 1, 0, 0],
[1, 2, 0, 1, 2, 0, 1, 0, 0], [1, 0, 2, 1, 2, 0, 1, 0, 0], [1, 2, 2, 1, 2, 0, 1, 0, 0],
[2, 2, 2, 0, 0, 1, 1, 0, 0]
*/
This is a java equivalent code sample
package testit;
public class TicTacToe {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 0 1 2
// 3 4 5
// 6 7 8
char[] pc = {' ' ,'o', 'x' };
char[] c = new char[9];
// initialize c
for (int i = 0; i < 9; i++)
c[i] = pc[0];
for (int i = 0; i < 3; i++) {
c[0] = pc[i];
for (int j = 0; j < 3; j++) {
c[1] = pc[j];
for (int k = 0; k < 3; k++) {
c[2] = pc[k];
for (int l = 0; l < 3; l++) {
c[3] = pc[l];
for (int m = 0; m < 3; m++) {
c[4] = pc[m];
for (int n = 0; n < 3; n++) {
c[5] = pc[n];
for (int o = 0; o < 3; o++) {
c[6] = pc[o];
for (int p = 0; p < 3; p++) {
c[7] = pc[p];
for (int q = 0; q < 3; q++) {
c[8] = pc[q];
int countx = 0;
int county = 0;
for(int r = 0 ; r<9 ; r++){
if(c[r] == 'x'){
countx = countx + 1;
}
else if(c[r] == 'o'){
county = county + 1;
}
}
if(Math.abs(countx - county) < 2){
if(won(c, pc[2])+won(c, pc[1]) == 1 ){
System.out.println(c[0] + " " + c[1] + " " + c[2]);
System.out.println(c[3] + " " + c[4] + " " + c[5]);
System.out.println(c[6] + " " + c[7] + " " + c[8]);
System.out.println("*******************************************");
}
}
}
}
}
}
}
}
}
}
}
}
public static int won(char[] c, char n) {
if ((c[0] == n) && (c[1] == n) && (c[2] == n))
return 1;
else if ((c[3] == n) && (c[4] == n) && (c[5] == n))
return 1;
else if ((c[6] == n) && (c[7] == n) && (c[8] == n))
return 1;
else if ((c[0] == n) && (c[3] == n) && (c[6] == n))
return 1;
else if ((c[1] == n) && (c[4] == n) && (c[7] == n))
return 1;
else if ((c[2] == n) && (c[5] == n) && (c[8] == n))
return 1;
else if ((c[0] == n) && (c[4] == n) && (c[8] == n))
return 1;
else if ((c[2] == n) && (c[4] == n) && (c[6] == n))
return 1;
else
return 0;
}
}
`
Below Solution generates all possible combinations using recursion
It has eliminated impossible combinations and returned 888 Combinations
Below is a working code Possible winning combinations of the TIC TAC TOE game
const players = ['X', 'O'];
let gameBoard = Array.from({ length: 9 });
const winningCombination = [
[ 0, 1, 2 ],
[ 3, 4, 5 ],
[ 6, 7, 8 ],
[ 0, 3, 6 ],
[ 1, 4, 7 ],
[ 2, 5, 8 ],
[ 0, 4, 8 ],
[ 2, 4, 6 ],
];
const isWinningCombination = (board)=> {
if((Math.abs(board.filter(a => a === players[0]).length -
board.filter(a => a === players[1]).length)) > 1) {
return false
}
let winningComb = 0;
players.forEach( player => {
winningCombination.forEach( combinations => {
if (combinations.every(combination => board[combination] === player )) {
winningComb++;
}
});
});
return winningComb === 1;
}
const getCombinations = (board) => {
let currentBoard = [...board];
const firstEmptySquare = board.indexOf(undefined)
if (firstEmptySquare === -1) {
return isWinningCombination(board) ? [board] : [];
} else {
return [...players, ''].reduce((prev, next) => {
currentBoard[firstEmptySquare] = next;
if(next !== '' && board.filter(a => a === next).length > (gameBoard.length / players.length)) {
return [...prev]
}
return [board, ...prev, ...getCombinations(currentBoard)]
}, [])
}
}
const startApp = () => {
let combination = getCombinations(gameBoard).filter(board =>
board.every(item => !(item === undefined)) && isWinningCombination(board)
)
printCombination(combination)
}
const printCombination = (combination)=> {
const ulElement = document.querySelector('.combinations');
combination.forEach(comb => {
let node = document.createElement("li");
let nodePre = document.createElement("pre");
let textnode = document.createTextNode(JSON.stringify(comb));
nodePre.appendChild(textnode);
node.appendChild(nodePre);
ulElement.appendChild(node);
})
}
startApp();
This discovers all possible combinations for tic tac toe (255,168) -- written in JavaScript using recursion. It is not optimized, but gets you what you need.
const [EMPTY, O, X] = [0, 4, 1]
let count = 0
let coordinate = [
EMPTY, EMPTY, EMPTY,
EMPTY, EMPTY, EMPTY,
EMPTY, EMPTY, EMPTY
]
function reducer(arr, sumOne, sumTwo = null) {
let func = arr.reduce((sum, a) => sum + a, 0)
if((func === sumOne) || (func === sumTwo)) return true
}
function checkResult() {
let [a1, a2, a3, b1, b2, b3, c1, c2, c3] = coordinate
if(reducer([a1,a2,a3], 3, 12)) return true
if(reducer([a1,b2,c3], 3, 12)) return true
if(reducer([b1,b2,b3], 3, 12)) return true
if(reducer([c1,c2,c3], 3, 12)) return true
if(reducer([a3,b2,c1], 3, 12)) return true
if(reducer([a1,b1,c1], 3, 12)) return true
if(reducer([a2,b2,c2], 3, 12)) return true
if(reducer([a3,b3,c3], 3, 12)) return true
if(reducer([a1,a2,a3,b1,b2,b3,c1,c2,c3], 21)) return true
return false
}
function nextPiece() {
let [countX, countO] = [0, 0]
for(let i = 0; i < coordinate.length; i++) {
if(coordinate[i] === X) countX++
if(coordinate[i] === O) countO++
}
return countX === countO ? X : O
}
function countGames() {
if (checkResult()) {
count++
}else {
for (let i = 0; i < 9; i++) {
if (coordinate[i] === EMPTY) {
coordinate[i] = nextPiece()
countGames()
coordinate[i] = EMPTY
}
}
}
}
countGames()
console.log(count)
I separated out the checkResult returns in case you want to output various win conditions.
Could be solved with brute force but keep in mind the corner cases like player2 can't move when player1 has won and vice versa. Also remember Difference between moves of player1 and player can't be greater than 1 and less than 0.
I have written code for validating whether provided combination is valid or not, might soon post on github.
If I have an array of golf results:
-3, +5, -3, 0, +1, +8, 0, +6, +2, -8, +5
I need to find a sequence of three adjacent numbers which have the minimum sum. For this example, the sub-sequences would be:
[-3, +5, -3]
[+5, -3, 0]
[-3, 0, +1]
... etc ...
[+2, -8, +5]
And the minimum sequence would be [-3, 0, +1] having a sum of -2.
You could use this LINQ query:
int[] golfResult = { -3, +5, -3, 0, +1, +8, 0, +6, +2, -8, +5 };
var combinations = from i in Enumerable.Range(0, golfResult.Length - 2)
select new {
i1 = golfResult[i],
i2 = golfResult[i + 1],
i3 = golfResult[i + 2],
};
var min = combinations.OrderBy(x => x.i1 + x.i2 + x.i3).First();
int[] minGolfResult = { min.i1, min.i2, min.i3 }; // -3, 0, +1
Of course you need to check if there are at least three results in the array.
I'm not sure why you would do this with LINQ. I think a straight up iterative solution is easier to understand:
int[] scores = new[] { -3, 5, -3, 0, 1, 8, 0, 6, 2, -8, 5 };
int minimumSubsequence = int.MaxValue;
int minimumSubsequenceIndex = -1;
for (int i = 0; i < scores.Length - 2; i++)
{
int sum = scores[i] + scores[i + 1] + scores[i + 2];
if (sum < minimumSubsequence)
{
minimumSubsequence = sum;
minimumSubsequenceIndex = i;
}
}
// minimumSubsequenceIndex is index of the first item in the minimum subsequence
// minimumSubsequence is the minimum subsequence's sum.
If you really want to do it in LINQ, you can go this way:
int length = 3;
var scores = new List<int>() { -3, +5, -3, 0, +1, +8, 0, +6, +2, -8, +5 };
var results =
scores
.Select((value, index) => new
{
Value = scores.Skip(index - length + 1).Take(length).Sum(),
Index = index - length + 1
})
.Skip(length - 1)
.OrderBy(x => x.Value)
.First()
.Index;
This creates a second list that sums all length preceeding elements and then sorts it. You have
Given N arrays with sizeof N, and they are all sorted, if it does not allow you to use extra space, how will find their common datas efficiently or with less time complexity?
For ex:
1. 10 160 200 500 500
2. 4 150 160 170 500
3. 2 160 200 202 203
4. 3 150 155 160 300
5. 3 150 155 160 301
This is an interview question, I found some questions which were similar but they didn't include the extra conditions of input being sorted or not being able to use extra memory.
I couldn't think of any solution less than O(n^2 lg n) complexity. In that case, I'd prefer to go with the simplest solution which gives me this complexity, which is:
not_found_flag = false
for each element 'v' in row-1
for each row 'i' in the remaining set
perform binary search for 'v' in 'i'
if 'v' not found in row 'i'
not_found_flag = true
break
if not not_found_flag
print element 'v' as it is one of the common element
We could improve this by comparing the min and max of each row and decide based on that whether it is possible for a number 'num' to fall between 'min_num' and 'max_num' of that row.
Binary search -> O(log n)
For searching 1 num in n-1 rows : O(nlogn)
Binary search for each number in first row : O(n2logn)
I selected first row, we can pick any row and if a no element of the row picked is found in any of the (N-1) rows then we don't really have common data.
It seems this can be done in O(n^2); i.e., just looking at each element once. Note that if an element is common to all the arrays then it must exist in any one of them. Also for purposes of illustration (and since you used the for loop above) I will assume we can keep an index for each of the arrays, but I'll talk about how to get around this later.
Let's call the arrays A_1 through A_N, and use indices starting at 1. Pseudocode:
# Initial index values set to first element of each array
for i = 1 to N:
x_i = 1
for x_1 = 1 to N:
val = A_1[x_1]
print_val = true
for i = 2 to N:
while A_i[x_i] < val:
x_i = x_i + 1
if A_i[x_i] != val:
print_val = false
if print_val:
print val
Explanation of algorithm. We use the first array (or any arbitrary array) as the reference algorithm, and iterate through all the other arrays in parallel (kind of like the merge step of a merge sort, except with N arrays.) Every value of the reference array that is common to all the arrays must be present in all the other arrays. So for each other array (since they are sorted), we increase the index x_i until the value at that index A_i[x_i] is at least the value we are looking for (we don't care about lesser values; they can't be common.) We can do this since the arrays are sorted and thus monotonically nondecreasing. If all the arrays had this value, then we print it, otherwise we increment x_1 in the reference array and keep going. We have to do this even if we don't print the value.
By the end, we've printed all the values that are common to all the arrays, while only having examined each element once.
Getting around the extra storage requirement. There are many ways to do this, but I think the easiest way would be to check the first element of each array and take the max as the reference array A_1. If they are all the same, print that value, and then store the indices x_2 ... x_N as the first element of each array itself.
Java implementation (for brevity, without the extra hack), using your example input:
public static void main(String[] args) {
int[][] a = {
{ 10, 160, 200, 500, 500, },
{ 4, 150, 160, 170, 500, },
{ 2, 160, 200, 202, 203, },
{ 3, 150, 155, 160, 300 },
{ 3, 150, 155, 160, 301 } };
int n = a.length;
int[] x = new int[n];
for( ; x[0] < n; x[0]++ ) {
int val = a[0][x[0]];
boolean print = true;
for( int i = 1; i < n; i++ ) {
while (a[i][x[i]] < val && x[i] < n-1) x[i]++;
if (a[i][x[i]] != val) print = false;
}
if (print) System.out.println(val);
}
}
Output:
160
This is a solution in python O(n^2), uses no extra space but destroys the lists:
def find_common(lists):
num_lists = len(lists)
first_list = lists[0]
for j in first_list[::-1]:
common_found = True
for i in range(1,num_lists):
curr_list = lists[i]
while curr_list[len(curr_list)-1] > j:
curr_list.pop()
if curr_list[len(curr_list)-1] != j:
common_found = False
break
if common_found:
return j
An O(n^2) (Python) version that doesn't use extra storage, but modify the original array. Allows to store the common elements without printing them:
data = [
[10, 160, 200, 500, 500],
[4, 150, 160, 170, 500],
[2, 160, 200, 202, 203],
[3, 150, 155, 160, 300],
[3, 150, 155, 160, 301],
]
for k in xrange(len(data)-1):
A, B = data[k], data[k+1]
i, j, x = 0, 0, None
while i<len(A) or j<len(B):
while i<len(A) and (j>=len(B) or A[i] < B[j]):
A[i] = x
i += 1
while j<len(B) and (i>=len(A) or B[j] < A[i]):
B[j] = x
j += 1
if i<len(A) and j<len(B):
x = A[i]
i += 1
j += 1
print data[-1]
What I'm doing is basically get every array in the data and comparing with it's next, element by element, removing those that are not common.
Here is the Java implementation
public static Integer[] commonElementsInNSortedArrays(int[][] arrays) {
int baseIndex = 0, currentIndex = 0, totalMatchFound= 0;
int[] indices = new int[arrays.length - 1];
boolean smallestArrayTraversed = false;
List<Integer> result = new ArrayList<Integer>();
while (!smallestArrayTraversed && baseIndex < arrays[0].length) {
totalMatchFound = 0;
for (int array = 1; array < arrays.length; array++) {
currentIndex = indices[array - 1];
while (currentIndex < arrays[array].length && arrays[array][currentIndex] < arrays[0][baseIndex]) {
currentIndex ++;
}
if (currentIndex < arrays[array].length) {
if (arrays[array][currentIndex] == arrays[0][baseIndex]) {
totalMatchFound++;
}
} else {
smallestArrayTraversed = true;
}
indices[array - 1] = currentIndex;
}
if (totalMatchFound == arrays.length - 1) {
result.add(arrays[0][baseIndex]);
}
baseIndex++;
}
return result.toArray(new Integer[0]);
}
Here is the Unit Tests
#Test
public void commonElementsInNSortedArrayTest() {
int arr[][] = { {1, 5, 10, 20, 40, 80},
{6, 7, 20, 80, 100},
{3, 4, 15, 20, 30, 70, 80, 120}
};
Integer result[] = ArrayUtils.commonElementsInNSortedArrays(arr);
assertThat(result, equalTo(new Integer[]{20, 80}));
arr = new int[][]{
{23, 34, 67, 89, 123, 566, 1000},
{11, 22, 23, 24,33, 37, 185, 566, 987, 1223, 1234},
{23, 43, 67, 98, 566, 678},
{1, 4, 5, 23, 34, 76, 87, 132, 566, 665},
{1, 2, 3, 23, 24, 344, 566}
};
result = ArrayUtils.commonElementsInNSortedArrays(arr);
assertThat(result, equalTo(new Integer[]{23, 566}));
}
This Swift solution makes a copy of the original but could be modified to take an inout parameter so that it takes no additional space. I left it as a copy because I think it is better to not modify the original since it deletes elements. It is possible to not remove elements by keeping indices, but this algorithm removes elements to keep track of where it is. This is a functional approach, and may not be super efficient but works. Since it is functional less conditional logic is necessary. I posted it because I thought it might be a different approach which might be interesting to others, and maybe others can figure out ways of making it more efficient.
func findCommonInSortedArrays(arr: [[Int]]) -> [Int] {
var copy = arr
var result: [Int] = []
while (true) {
// get first elements
let m = copy.indices.compactMap { copy[$0].first }
// find max value of those elements.
let mm = m.reduce (0) { max($0, $1) }
// find the value in other arrays or nil
let ii = copy.indices.map { copy[$0].firstIndex { $0 == mm } }
// if nil present in of one of the arrays found, return result
if (ii.map { $0 }).count != (ii.compactMap { $0 }.count) { return result }
// remove elements that don't match target value.
copy.indices.map { copy[$0].removeFirst( ii[$0] ?? 0 ) }
// add to list of matching values.
result += [mm]
// remove the matched element from all arrays
copy.indices.forEach { copy[$0].removeFirst() }
}
}
findCommonInSortedArrays(arr: [[9, 10, 12, 13, 14, 29],
[3, 5, 9, 10, 13, 14],
[3, 9, 10, 14]]
)
findCommonInSortedArrays(arr: [[],
[],
[]]
)
findCommonInSortedArrays(arr: [[9, 10, 12, 13, 14, 29],
[3, 5, 9, 10, 13, 14],
[3, 9, 10, 14],
[9, 10, 29]]
)
findCommonInSortedArrays(arr: [[9, 10, 12, 13, 14, 29],
[3, 5, 9, 10, 13, 14],
[3, 9, 10, 14],
[9, 10, 29]]
)