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

/*==========================================================================
*   	AUTHOR:    RonWonWon
*   	CREATED:   13.06.2021 22:36:10        
/*==========================================================================*/
import java.io.*;
import java.util.*;

public class D {
	
	public static void main(String[] args) throws IOException {
		int n = in.nextInt();
		ArrayList<Pair> edge = new ArrayList<>();
		out.println("? 1"); out.flush();
		int a[] = in.readArray(n);
		int leaf = 0;
		int cur = 0;
		int vis[] = new int[n+1];
		vis[1] = 1;
		HashSet<Integer> has[] = new HashSet[n+1];
		for(int i=0;i<=n;i++) has[i] = new HashSet<>();
		Pair dist[] = new Pair[n];
		for(int i=0;i<n;i++){
			if(a[i]==2){
				leaf = i+1;
			}
			if(a[i]==0) cur = i+1;
			dist[i] = new Pair(i+1,a[i]);
		}
		Arrays.sort(dist);
		for(int i=0;i<n-1;i++){
			if(dist[i].y!=0){
				if(dist[i].y==1&&has[cur].contains(dist[i].x)==false){
					edge.add(new Pair(dist[i].x,cur));
					has[dist[i].x].add(cur); has[cur].add(dist[i].x);
				}
			}
		}
		//for(Pair i : edge) out.println(i.x+" "+i.y);
		//out.flush();
		while(edge.size()<n-1){
			cur = leaf;
			out.println("? "+cur); out.flush();
			vis[cur] = 1;
			a = in.readArray(n);
			leaf = 0;
  		cur = 0;
  		dist = new Pair[n];
  		for(int i=0;i<n;i++){
  			if(a[i]==2&&vis[i+1]==0){
  				leaf = i+1;
  			}
  			if(a[i]==0) cur = i+1;
  			dist[i] = new Pair(i+1,a[i]);
  		}
  		Arrays.sort(dist);
  		for(int i=0;i<n-1;i++){
  			if(dist[i].y!=0){
  				if(dist[i].y==1&&has[cur].contains(dist[i].x)==false){
  					edge.add(new Pair(dist[i].x,cur));
						has[dist[i].x].add(cur); has[cur].add(dist[i].x);
  				}
  			}
  		}	
		}
		out.println("!"); out.flush();
		for(Pair i : edge) out.println(i.x+" "+i.y);
		out.flush();
	}
	
	static class Pair implements Comparable<Pair> {
		int x, y;
		Pair(int a, int b){ x = a; y = b; }		
		@Override
		public int compareTo(Pair o) {
			if(o.y - this.y>0)
				return -1;
			else if(o.y - this.y<0)
				return 1;
			else 
				return 0;
		}
	}
	
	static FastScanner in = new FastScanner();
  static PrintWriter out = new PrintWriter(System.out);
	static int oo = Integer.MAX_VALUE;
	
	static class FastScanner {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer("");
		
		void ini() throws FileNotFoundException {
			br = new BufferedReader(new FileReader("input.txt"));
		}
		
		String next() {
			while(!st.hasMoreTokens())
				try { st = new StringTokenizer(br.readLine()); }
				catch(IOException e) {}
			return st.nextToken();
		}
		
		String nextLine(){
			try{ return br.readLine(); } 
			catch(IOException e) { } return "";
		}
		
		int nextInt() {
			return Integer.parseInt(next());
		}
		
		int[] readArray(int n) {
			int a[] = new int[n];
			for(int i=0;i<n;i++) a[i] = nextInt();
			return a;
		}
		
		long nextLong() {
			return Long.parseLong(next());
		}
		
		long[] readArrayL(int n) {
			long a[] = new long[n];
			for(int i=0;i<n;i++) a[i] = nextLong();
			return a;
		}
		
		double nextDouble() {
			return Double.parseDouble(next());
		}
		
		double[] readArrayD(int n) {
			double a[] = new double[n];
			for(int i=0;i<n;i++) a[i] = nextDouble();
			return a;
		}
	}

	static final Random random = new Random();

	static void ruffleSort(int[] a){
		int n = a.length;
		for(int i=0;i<n;i++){
			int j = random.nextInt(n), temp = a[j];
			a[j] = a[i]; a[i] = temp;
		}
		Arrays.sort(a); 	
	}
	
	static void ruffleSortL(long[] a){
		int n = a.length;
		for(int i=0;i<n;i++){
			int j = random.nextInt(n);
			long temp = a[j];
			a[j] = a[i]; a[i] = temp;
		}
		Arrays.sort(a); 	
	}
}
