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;

#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp> // Including tree_order_statistics_node_update
using namespace __gnu_pbds;
template<class T> using ordered_set = tree<T, null_type , less<T> , rb_tree_tag , tree_order_statistics_node_update> ;
// find_by_order
//order_of_key

// endl actually works as cout<<"\n"<<flush;
//use external for-loop(in driver function) to avoid memory limit exceeded in choice dp
// various forms of dp
//1. choice dp  =>  hamesha i+1 pr mat jayo i+k something pr directly bhi jaa skte ha
//3. (i,j) where i=0,n-1 dp
//4. expected value dp  => sigma on har case (probability*(no of expected value of lower state))
//7. use iterative version for optimization

#define ff              first
#define ss              second
#define int             long long
#define pb              push_back
#define rb              pop_back
#define mp              make_pair
#define pii             pair<int,int>
#define vi              vector<int>
#define mii             map<int,int>
#define un_mii          unordered_map<int,int>
#define pqb             priority_stack<int>
#define pqs             priority_stack<int,vi,greater<int> >
#define setbits(x)      __builtin_popcountll(x)
#define zrobits(x)      __builtin_ctzll(x)
#define mod             1000000007
// #define mod             998244353
#define inf             1e18
#define ps(x,y)         fixed<<setprecision(y)<<x
#define all(x)          (x).begin(),(x).end()
#define pi              3.14159265358979323846

int pow_mod(int a, int b, int n) {
    int result = 1;
    while (b > 0) {
        if ((b & 1) == 1) {
            result = ((result % n) * (a % n)) % n;
        }
        a = ((a % n) * (a % n)) % n;
        b = b >> 1;
    }
    return result % n;
}
int pow_mod_recursion(int a, int b, int n) {
    if (b == 0) return 1;
    long long ans = pow_mod(a, b / 2, n) % n;
    ans = ((ans % n) * (ans % n)) % n;
    if (b & 1) ans = ((ans % n) * (a)) % n;
    return ans % n;
}
int inverseModulo(int a, int m) {
    return pow_mod(a, m - 2, m);
}

int power(int a, int b) {
    int result = 1;
    while (b > 0) {
        if (b & 1) {
            result *= a;
        }
        a = a * a;
        b >>= 1;
    }
    return result;
}

int gcd(int a, int b) {
    if (b == 0) {
        return a;
    }

    return gcd(b, a % b);
}

int add(int a, int b) {

    return (a + b) % mod;
}
int subtract(int a, int b) {

    int ans = (a - b) % mod;
    if (ans < 0) {
        ans = (ans + mod) % mod;
    }

    return ans;
}

// NCR

// int fact[11]={0};
// int invfact[11]={0};
// void preCompute(){
//   fact[0]=1;
//   fact[1]=1;
//   for(int i=2;i<11;i++){
//     fact[i]=((fact[i-1]%mod)*(i%mod))%mod;
//   }
// for(int i=0;i<200005;i++){
// invfact[i]=inverseModulo(fact[i],mod);
// }
// }
// int ncr(int n, int r){
// int ans=((fact[n]%mod)*((invfact[n-r]*invfact[r])%mod))%mod;
// return ans;
// }


// PRIME SEIVE

// const int N = 100001;

// int spf[N];
// vi primes;
// void seive() {
// // for(int i=0;i<N;i++){
//     // spf[i] = i;
// // }
// // pre[1]=1;
//     memset(spf, -1, sizeof(spf));
//     for (int i = 2; i < N; i++) {
//         if (spf[i] == -1) {
//             primes.pb(i);
//             // pre[i] = 1;
//             for (int j = i * i; j < N; j += i) {
//                 if (spf[j] == -1) spf[j] = i;
//             }
//         }
//     }
// // for(int i=1;i<N;i++){
// //     pre[i]+=pre[i-1];
// // }
// }

// sum of divisors
// int sum[N];
// void calSum() {
//     seive();
//     memset(ans, -1, sizeof(ans));
//     sum[1] = 1;
//     for (int i = 2; i < N; i++) {
//         if (spf[i] == -1) {
//             sum[i] = i + 1;
//         }
//         else {
//             int j = i;
//             int d = spf[i];
//             sum[i] = 1; // sum
//             while (j % d == 0) {
//                 j /= d;
//                 sum[i] = sum[i] * d + 1;
//             }
//             sum[i] *= sum[j];
//         }
//     }
// }

// Number of Divisors;

// int num[N];

// void divisors(){
//   seive();
//   num[1]=1;
//   for(int i=2;i<N;i++){
//     num[i]=1;
//     if(spf[i]==i) num[i]=2;
//     else{
//       int k=i;
//       while(k!=1){
//         int p=0;
//         int d=spf[k];
//         while(k%d==0){
//           k/=d;
//           p++;
//         }
//         num[i]*=(p+1);
//       }
//     }
//   }
// }


// CRT
// vi num(2,1);
//   num[0]=2;
//   num[1]=5;

// vi rem(2,1);
//   rem[0]=r1;
//   rem[1]=r2;

// int CRT(vi & num, vi & rem){
//     int n=num.size();

//     vi ppi(n,1);

//     int prod=1;

//     for(int i=0;i<n;i++){
//         prod=prod*num[i];
//     }

//     for(int i=0;i<n;i++){
//         ppi[i]=prod/num[i];
//     }

//     vi inv(n,1);
//     for(int i=0;i<n;i++){
//         inv[i]=pow_mod(ppi[i],num[i]-2,num[i]);
//     }

//     int ans=0;

//     for(int i=0;i<n;i++){
//         ans=((ans%prod)+((ppi[i]%prod)*(inv[i]%prod)*(rem[i]%prod))%prod)%prod;
//     }

//     return ans;
// }


// Interactive Problem
int n;
set<int> s;
set<pii> edgelist;
void query(int i, vector<int>&v) {
    cout << "? " << " " << i << endl;
    int ans;
    for (int i = 0; i < n; i++) {
        cin >> ans;
        v.pb(ans);
    }
}

void solve(int numCase) {
    cin >> n;
    vector<vector<int>> arr;
    for (int i = 1; i <= n; i++) {
        s.insert(i);
    }
    for (int i = 1; i <= (n + 1) / 2; i++) {
        vector<int> v;
        query(i, v);
        arr.pb(v);
        for (int j = 1; j <= n; j++) {
            if (v[j - 1] == 1) {
                edgelist.insert({min(j, i), max(i , j)});
                if (s.count(i) != 0) {
                    s.erase(s.find(i));
                }
                if (s.count(j) != 0) {
                    s.erase(s.find(j));
                }
            }
        }
    }
    if (edgelist.size() != (n - 1)) {
        int x = *s.begin();
        map<int, int>f;

        for (int i = 1; i <= (n + 1) / 2; i++) {
            int d = arr[i - 1][x - 1];
            for (int j = 1; j <= n; j++) {
                if (arr[i - 1][j - 1] == (d - 1)) {
                    f[j]++;
                }
            }
        }
        int node = 0;
        int cnt = 0;
        for (auto t : f) {
            if (t.ss > cnt) {
                node = t.ff;
                cnt = t.ss;
            }
        }
        edgelist.insert({node, x});
    }

    cout << "!\n";
    for (auto x : edgelist) {
        cout << x.ff << " " << x.ss << endl;
    }
    edgelist.clear();
    s.clear();
}
int32_t main() {

    // to buffer up output, so output function will be called only once when program terminates.
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);

#ifndef ONLINE_JUDGE
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
#endif

    int t = 1;
    // cin >> t;
    int c = 1;
    while (t--) {
        solve(c);
        c++;
    }

    return 0;
}