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

#include <bits/stdc++.h>
template <typename T>
inline T abs(T x) {
  return x < 0 ? -x : x;
}
template <typename T>
inline T ma(T &x, T y) {
  return x < y ? x = y : T();
}
template <typename T>
inline T mi(T &x, T y) {
  return x > y ? x = y : T();
}
using namespace std;
struct Read {
  template <typename T>
  inline const Read &operator()(T &x) const {
    int s = 1, d = getchar();
    x = 0;
    for (; !isdigit(d); d = getchar())
      if (!(d ^ '-')) s = -1;
    while (isdigit(d)) x = x * 10 + (d & 15), d = getchar();
    x *= s;
    return *this;
  }
} read;
inline int gi() {
  int x;
  read(x);
  return x;
}
template <class T>
void write(T x) {
  if (x < 0) {
    putchar('-');
    x = -x;
  }
  if (x > 9) write(x / 10);
  putchar(x % 10 + '0');
}
const int T = 1e2 + 10;
const int N = 2e3 + 10;
const int M = 6e5 + 10;
const int K = 3e2 + 10;
const int LG = 20;
const int Q = N * 2;
const int P = 19950501;
const int base = 19491001;
const int inf = ~0U >> 2;
const long long infll = ~0ULL >> 1;
const double eps = 1e-9;
const int dx[] = {0, 0, 1, -1};
const int dy[] = {1, -1, 0, 0};
int n, r[N][N], p[N], d[N], sz[N], _intree[N][N];
pair<int, int> a[N];
vector<int> E[N];
void query(int x) {
  printf("? %d\n", x);
  fflush(stdout);
  for (int _a = (n), i = 1; i <= _a; ++i) r[x][i] = gi();
}
void me(int x, int y) {
  E[x].push_back(y);
  p[y] = x;
  d[y] = d[x] + 1;
}
bool intree(int x, int rt) {
  if (~_intree[x][rt]) return _intree[x][rt];
  return _intree[x][rt] =
             p[rt] ? r[p[rt]][x] + d[p[rt]] == r[rt][x] + d[rt] : 1;
}
int md;
void solve(int x, bool f) {
  if (x ^ 1 && d[x] == md) return;
  if (f) query(x);
  if (x == 1)
    for (int _a = (n), i = 1; i <= _a; ++i) md = max(md, r[x][i]);
  vector<pair<int, int> > a;
  for (int _a = (n), i = 1; i <= _a; ++i)
    if (x == i || intree(i, x)) a.push_back(make_pair((r[x][i]), (i)));
  if (a.size() == 1) return;
  sort(a.begin(), a.end());
  int i = 1, t = a.size();
  while (i + 1 == t || a[i].first + 1 == a[i + 1].first) {
    me(a[i - 1].second, a[i].second);
    p[a[i].second] = x;
    for (int _a = (n), j = 1; j <= _a; ++j)
      _intree[j][a[i].second] = intree(j, a[i - 1].second);
    _intree[a[i - 1].second][a[i].second] = 0;
    for (int _a = (n), j = 1; j <= _a; ++j)
      r[a[i].second][j] =
          r[x][j] + d[x] - d[a[i].second] + (_intree[j][a[i].second] ? 0 : 2);
    i++;
  }
  for (int j = i; j < t; ++j) {
    if (a[j].first != a[i].first) break;
    me(a[i - 1].second, a[j].second);
    p[a[j].second] = x;
  }
  int nx = a[i - 1].second;
  if (E[nx].size() < 1) return;
  for (int _a = (E[nx].size() - 1), i = 0; i < _a; ++i) solve(E[nx][i], 1);
  int lx = E[nx].back();
  for (int _a = (n), i = 1; i <= _a; ++i) _intree[i][lx] = intree(i, nx);
  for (int _a = (E[nx].size() - 1), i = 0; i < _a; ++i) {
    for (int _a = (n), j = 1; j <= _a; ++j)
      _intree[j][lx] &= !intree(j, E[nx][i]);
  }
  _intree[nx][lx] = 0;
  for (int _a = (n), i = 1; i <= _a; ++i)
    r[lx][i] = r[nx][i] + d[nx] - d[lx] + (_intree[i][lx] ? 0 : 2);
  solve(lx, 0);
}
int main() {
  read(n);
  memset(_intree, -1, sizeof(_intree));
  solve(1, 1);
  puts("!");
  for (int _a = (n), i = 1; i <= _a; ++i)
    for (int _a = (E[i].size()), j = 0; j < _a; ++j)
      printf("%d %d\n", i, E[i][j]);
}
