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

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
import java.util.StringTokenizer;

import static java.lang.Math.*;

public class D {
    static class FastScanner {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public FastScanner() {
            reader = new BufferedReader(new InputStreamReader(System.in), 32768);
            tokenizer = null;
        }

        public String next() {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return tokenizer.nextToken();
        }

        public int nextInt() {
            return Integer.parseInt(next());
        }

        public long nextLong() {
            return Long.parseLong(next());
        }

        public double nextDouble() {
            return Double.parseDouble(next());
        }

        public String nextLine() {
            try {
                return reader.readLine();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
    public static void main(String args[]) throws Exception {
        FastScanner sc = new FastScanner();
        int T = 1;
//        T = sc.nextInt();
        PrintWriter pw = new PrintWriter(System.out);
        StringBuilder sb = new StringBuilder();
        while (T-- > 0) {
            solve(sc, pw, sb);
        }
        pw.print(sb);
        pw.close();
    }

    public static void solve(FastScanner sc, PrintWriter pw, StringBuilder sb) throws Exception {
        int n=sc.nextInt();
        int[][] graph = new int[n+1][n+1];
        int[] arr =new int[n+1];
        arr=query(1,n,sc,pw);
        Queue<Integer> q = new LinkedList<>();
        boolean[] vis = new boolean[n+1];
        MakeGraph(n,graph,arr,1,q,vis);
        int cnt_white=0,cnt_black=0;
        for(int i=1;i<=n;i++){
            if((arr[i]&1)>0) cnt_black++;
            else cnt_white++;
        }
        if(cnt_black<cnt_white){
            for(int i=1;i<=n;i++){
                if((arr[i]&1)>0) q.add(i);
            }
        }
        else{
            for(int i=1;i<=n;i++){
                if((arr[i]&1)==0  && i!=1) q.add(i);
            }
        }
        int cnt=0;
        while(cnt<(n+1)/2){
            cnt++;
            if(q.isEmpty()) break;
            int node=q.poll();
            vis[node]=true;
//            System.out.println("node = " + node);
            arr=query(node,n,sc,pw);
            MakeGraph(n,graph,arr,node,q,vis);
        }
        System.out.println("!");
        for(int i=1;i<=n;i++){
            for(int j=i+1;j<=n;j++){
                if(graph[i][j]>0) System.out.println(i+" "+j);
            }
        }
    }

    private static void MakeGraph(int n, int[][] graph, int[] arr,int node,Queue<Integer> q,boolean[] vis) {
        for(int i = 1; i<= n; i++){
            if(arr[i]==1){
                graph[node][i]=1;
                graph[i][node]=1;
            }
        }
    }

    public static int[] query(int node,int n,FastScanner sc, PrintWriter pw){
        System.out.println("? "+node);
        int[] arr = new int[n+1];
        for(int i=1;i<=n;i++) arr[i]=sc.nextInt();
        return arr;
    }
}

//guess the edges of an undirected graph of n nodes (1...n)
// picks a node r and gets a reply as an array (d1....dn) di length of shortest path from r to i
// at most ceil(n/2) queries
// di =1 the nodes are connected to r but this requires at most n queries so we need to optimise it
// all nodes can be considered as 2-colored hence we need either the white nodes or the black nodes