5
5 3
2 4
3 1
4 5
 4
2 4
3 2
2 1
 5
5 3
2 4
3 1
4 5
 2
1 2
 4
3 4
2 3
3 1
 10
8 10
2 9
7 8
4 1
7 3
4 7
2 5
2 4
4 6
 5
3 4
3 2
3 1
1 5
 3
1 2
2 3
 5
1 2
1 3
1 4
1 5

1 2
2 3
2 4
 1 3
2 4
3 5
4 5
 1 2
 1 3
2 3
3 4
 1 4
2 4
2 5
2 9
3 7
4 6
4 7
7 8
8 10
 1 3
1 5
2 3
3 4
 1 2
2 3
 1 2
1 3
1 4
1 5

#!/usr/bin/env python3
# from typing import *

import sys
import io
import math
import collections
import decimal
import itertools
import bisect
import heapq


def input():
    return sys.stdin.readline()[:-1]


# sys.setrecursionlimit(1000000)

# _INPUT = """# paste here...
# """
# sys.stdin = io.StringIO(_INPUT)

class Node:
    def __init__(self, _i):
        self.i = _i
        self.parent = -1
        self.children = set()
        self.dist2 = set()

def query(i):
    print('?', i+1)
    sys.stdout.flush()
    return list(map(int, input().split()))

def unite(parent, child):
    Nodes[child].parent = parent
    Nodes[parent].children.add(child)
    Edges.append((parent, child))

def dfs(i, grand_parent):
    L = query(i)
    for j in range(N):
        if L[j] == 1:
            if grand_parent != -1 and j in Nodes[grand_parent].children:
                unite(j, i)
            else:
                unite(i, j)

    for j in range(N):
        if L[j] == 2:
            if j == grand_parent:
                continue
            if grand_parent != -1 and j in Nodes[grand_parent].dist2:
                unite(Nodes[i].parent, j)
            else:
                Nodes[i].dist2.add(j)

    for j in Nodes[i].dist2:
        if j != grand_parent and Nodes[j].parent == -1 and j not in Nodes[Nodes[i].parent].children:
            dfs(j, i)


N = int(input())
# G = [list() for _ in range(N)]
Edges = []
Nodes = [Node(i) for i in range(N)]
Nodes[0].parent = -1

dfs(0, -1)

print('!')
for i, j in Edges:
    print(i+1, j+1)

sys.stdout.flush()
