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>
using namespace std;
int ni() {
  int v;
  scanf("%d", &v);
  return v;
}
long long int nl() {
  long long int v;
  scanf("%lld", &v);
  return v;
}
char nc() {
  char c;
  scanf("%c", &c);
  return c;
}
float nf() {
  float f;
  scanf("%f", &f);
  return f;
}
double nd() {
  double d;
  scanf("%lf", &d);
  return d;
}
vector<char> _buff;
string ns(uint32_t maxSize = 65536) {
  if (_buff.size() < maxSize) {
    _buff.resize(maxSize);
  }
  scanf("%s", _buff.data());
  return string(_buff.data());
}
void solve() {
  int n = ni();
  vector<int> depth(n + 1);
  vector<vector<int>> withDepth(n + 1, vector<int>());
  vector<int> parent(n + 1, -1);
  printf("? 1\n");
  fflush(stdout);
  for (int i = 1; i <= n; i++) {
    depth[i] = ni();
    withDepth[depth[i]].push_back(i);
  }
  for (int crtDepth = 1; crtDepth <= n - 1; crtDepth++) {
    const auto& nodes = withDepth[crtDepth];
    const auto& parents = withDepth[crtDepth - 1];
    if (parents.size() == 1) {
      for (auto node : nodes) {
        parent[node] = parents[0];
      }
      continue;
    }
    for (int node : withDepth[crtDepth]) {
      if (parent[node] != -1) {
        continue;
      }
      printf("? %d\n", node);
      fflush(stdout);
      vector<int> d(n);
      for (int i = 1; i <= n; i++) {
        d[i] = ni();
        if (d[i] == 1) {
          if (depth[i] == crtDepth - 1) {
            parent[node] = i;
          } else {
            parent[i] = node;
          }
        }
      }
    }
  }
  printf("!\n");
  for (int i = 2; i <= n; i++) {
    printf("%d %d\n", parent[i], i);
  }
  fflush(stdout);
}
int main() {
  solve();
  return 0;
}
