package main
import (
"bufio"
"fmt"
"os"
"sort"
"strconv"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
scanner.Split(bufio.ScanWords)
if !scanner.Scan() {
return
}
n_str := scanner.Text()
n, _ := strconv.Atoi(n_str)
is_F := make([]bool, 6*n+1)
for i := 0; i < 3*n; i++ {
scanner.Scan()
val, _ := strconv.Atoi(scanner.Text())
is_F[val] = true
}
type Block struct {
id int
owner byte
cards []int
parent_char int
}
var stack []int
var blocks []*Block
char_to_block := make([]int, 6*n+1)
block_id := 0
for i := 1; i <= 6*n; i++ {
stack = append(stack, i)
sz := len(stack)
if sz >= 3 {
c1 := stack[sz-3]
c2 := stack[sz-2]
c3 := stack[sz-1]
if is_F[c1] == is_F[c2] && is_F[c2] == is_F[c3] {
owner := byte('G')
if is_F[c1] {
owner = 'F'
}
b := &Block{
id: block_id,
owner: owner,
cards: []int{c1, c2, c3},
}
block_id++
stack = stack[:sz-3]
if len(stack) > 0 {
b.parent_char = stack[len(stack)-1]
}
blocks = append(blocks, b)
char_to_block[c1] = b.id
char_to_block[c2] = b.id
char_to_block[c3] = b.id
}
}
}
adj := make([][]int, 2*n)
in_degree := make([]int, 2*n)
for _, b := range blocks {
if b.parent_char != 0 {
p_id := char_to_block[b.parent_char]
adj[b.id] = append(adj[b.id], p_id)
in_degree[p_id]++
}
}
depth := make([]int, 2*n)
var get_depth func(int) int
get_depth = func(u int) int {
if depth[u] != 0 {
return depth[u]
}
max_d := 0
for _, v := range adj[u] {
d := get_depth(v)
if d > max_d {
max_d = d
}
}
depth[u] = max_d + 1
return depth[u]
}
for i := 0; i < 2*n; i++ {
get_depth(i)
}
var avail_F []int
var avail_G []int
for i := 0; i < 2*n; i++ {
if in_degree[i] == 0 {
if blocks[i].owner == 'F' {
avail_F = append(avail_F, i)
} else {
avail_G = append(avail_G, i)
}
}
}
var path []int
var dfs func(turn int) bool
dfs = func(turn int) bool {
if turn > 2*n {
return true
}
if turn%2 == 1 {
sort.Slice(avail_F, func(i, j int) bool {
return depth[avail_F[i]] > depth[avail_F[j]]
})
curr_avail := append([]int(nil), avail_F...)
for i := 0; i < len(curr_avail); i++ {
u := curr_avail[i]
idx := -1
for j, val := range avail_F {
if val == u {
idx = j
break
}
}
new_avail_F := append([]int(nil), avail_F[:idx]...)
new_avail_F = append(new_avail_F, avail_F[idx+1:]...)
var unlocked_F, unlocked_G []int
for _, v := range adj[u] {
in_degree[v]--
if in_degree[v] == 0 {
if blocks[v].owner == 'F' {
unlocked_F = append(unlocked_F, v)
} else {
unlocked_G = append(unlocked_G, v)
}
}
}
old_avail_F := avail_F
old_avail_G := avail_G
avail_F = append(new_avail_F, unlocked_F...)
avail_G = append(avail_G, unlocked_G...)
path = append(path, u)
if dfs(turn + 1) {
return true
}
path = path[:len(path)-1]
avail_F = old_avail_F
avail_G = old_avail_G
for _, v := range adj[u] {
in_degree[v]++
}
}
} else {
sort.Slice(avail_G, func(i, j int) bool {
return depth[avail_G[i]] > depth[avail_G[j]]
})
curr_avail := append([]int(nil), avail_G...)
for i := 0; i < len(curr_avail); i++ {
u := curr_avail[i]
idx := -1
for j, val := range avail_G {
if val == u {
idx = j
break
}
}
new_avail_G := append([]int(nil), avail_G[:idx]...)
new_avail_G = append(new_avail_G, avail_G[idx+1:]...)
var unlocked_F, unlocked_G []int
for _, v := range adj[u] {
in_degree[v]--
if in_degree[v] == 0 {
if blocks[v].owner == 'F' {
unlocked_F = append(unlocked_F, v)
} else {
unlocked_G = append(unlocked_G, v)
}
}
}
old_avail_F := avail_F
old_avail_G := avail_G
avail_G = append(new_avail_G, unlocked_G...)
avail_F = append(avail_F, unlocked_F...)
path = append(path, u)
if dfs(turn + 1) {
return true
}
path = path[:len(path)-1]
avail_F = old_avail_F
avail_G = old_avail_G
for _, v := range adj[u] {
in_degree[v]++
}
}
}
return false
}
dfs(1)
for _, u := range path {
c := blocks[u].cards
sort.Ints(c)
fmt.Printf("%d %d %d\n", c[0], c[1], c[2])
}
}