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.util.*;
import java.io.*;

public class D
{

	static FastIO f;

	public static void main(String args[]) throws IOException
	{
		f = new FastIO();

		int n = f.ni(), i, c;
		Pair d[];
		GraphInteger g = new GraphInteger(n);
		Queue<Integer> q = new LinkedList<>();
		boolean v[] = new boolean[n];
		v[0] = true;
		q.add(1);

		while(!q.isEmpty())
		{
			c = q.remove();
			d = query(c, n);

			update(v, d, q, g, c);
			// f.err(Arrays.toString(d) + "\n");
		}

		f.out("!\n");
		for(EdgeInteger e : g.h)
		{
			f.out(e.a + " " + e.b + "\n");
		}

		f.flush();
	}

	public static void update(boolean[] v, Pair[] d, Queue<Integer> q, GraphInteger g, int c)
	{
		int r = -1, p = c;
		int i;

		for(i = 1; i < d.length; i++)
		{

			if(d[i].a == d[i-1].a+1)
				p = d[i-1].b;

			if(d[i].a == 1)
			{
				g.add(c, d[i].b);
				v[d[i].b-1] = true;
			}
			else if(d[i].a == r && !v[d[i].b])
			{
				q.add(d[i].b);
				v[d[i].b-1] = true;
			}
			else if(r == -1)
			{
				g.add(p, d[i].b);
				v[d[i].b-1] = true;
			}

			if(r == -1 && d[i].a != d[i-1].a+1)
				r = d[i].a+1;
		}
	}

	public static Pair[] query(int r, int n) throws IOException
	{
		f.out("? " + r + "\n");
		Pair[] d = new Pair[n];

		for(int i = 0; i < n; i++)
			d[i] = new Pair(f.ni(), i+1);

		Arrays.sort(d, Pair.comp);

		return d;
	}

	static class Pair
	{
		int a, b;
		static Comparator<Pair> comp = new Comparator<Pair>(){

			@Override
			public int compare(Pair a, Pair b)
			{
				return a.a - b.a;
			}
		};
	
		Pair(int x, int y)
		{
			a = x;
			b = y;
		}
	
		@Override
		public int hashCode()
		{
			return super.hashCode();
		}
	
		@Override
		public boolean equals(Object obj)
		{
			Pair that = (Pair)obj;
	
			return super.equals(obj);
		}
	
		@Override
		public String toString()
		{
			return "[" + a + ", " + b + "]";
		}
	}

	static class GraphInteger
	{
		int v, c;
		HashMap<Integer, HashSet<Integer>> adj;
		HashSet<EdgeInteger> h;
	
		GraphInteger(int x)
		{
			v = x;
			c = 0;
			adj = new HashMap<>();
			h = new HashSet<>();
		}
	
		public void addEdge(Integer a, Integer b)
		{
			if(!adj.containsKey(a))
				adj.put(a, new HashSet<>());
			adj.get(a).add(b);
		}

		public void add(Integer a, Integer b)
		{
			addEdge(a, b);
			addEdge(b, a);
			h.add(new EdgeInteger(a, b));
			c++;
		}
	}

	static class EdgeInteger
	{
		Integer a, b;
	
		EdgeInteger(Integer x, Integer y)
		{
			a = x;
			b = y;
		}
	
		@Override
		public int hashCode()
		{
			return super.hashCode();
		}
	
		@Override
		public boolean equals(Object obj)
		{
			EdgeInteger that = (EdgeInteger)obj;
	
			return super.equals(obj);
		}
	
		@Override
		public String toString()
		{
			return "[" + a + " -> " + b + "]";
		}
	}

	public static class FastIO
	{
		BufferedReader br;
		BufferedWriter bw, be;
		StringTokenizer st;

		public FastIO()
		{
			br = new BufferedReader(new InputStreamReader(System.in));
			bw = new BufferedWriter(new OutputStreamWriter(System.out));
			be = new BufferedWriter(new OutputStreamWriter(System.err));
			st = new StringTokenizer("");
		}

		private void read() throws IOException
		{
			st = new StringTokenizer(br.readLine());
		}

		public String ns() throws IOException
		{
			while(!st.hasMoreTokens())
				read();
			return st.nextToken();
		}

		public int ni() throws IOException
		{
			return Integer.parseInt(ns());
		}

		public long nl() throws IOException
		{
			return Long.parseLong(ns());
		}

		public float nf() throws IOException
		{
			return Float.parseFloat(ns());
		}

		public double nd() throws IOException
		{
			return Double.parseDouble(ns());
		}

		public char nc() throws IOException
		{
			return ns().charAt(0);
		}

		public int[] nia(int n) throws IOException
		{
			int[] a = new int[n];
			for(int i = 0; i < n; i++)
				a[i] = ni();

			return a;
		}

		public long[] nla(int n) throws IOException
		{
			long[] a = new long[n];
			for(int i = 0; i < n; i++)
				a[i] = nl();

			return a;
		}

		public char[] nca() throws IOException
		{
			return ns().toCharArray();
		}

		public void out(String s) throws IOException
		{
			bw.write(s);
			bw.flush();
		}

		public void flush() throws IOException
		{
			bw.flush();
			be.flush();
		}

		public void err(String s) throws IOException
		{
			be.write(s);
			be.flush();
		}
	}
}