Problem:
Score : 300 points

Problem Statement

There are N balls in a two-dimensional plane. The i-th ball is at coordinates (x_i, y_i).

We will collect all of these balls, by choosing two integers p and q such that p \neq 0 or q \neq 0 and then repeating the following operation:

Choose a ball remaining in the plane and collect it. Let (a, b) be the coordinates of this ball. If we collected a ball at coordinates (a - p, b - q) in the previous operation, the cost of this operation is 0. Otherwise, including when this is the first time to do this operation, the cost of this operation is 1.

Find the minimum total cost required to collect all the balls when we optimally choose p and q.

Constraints

1 \leq N \leq 50

|x_i|, |y_i| \leq 10^9

If i \neq j, x_i \neq x_j or y_i \neq y_j.

All values in input are integers.

Input

Input is given from Standard Input in the following format:

N
x_1 y_1
:
x_N y_N

Output

Print the minimum total cost required to collect all the balls.

Sample Input 1

2
1 1
2 2

Sample Output 1

1

If we choose p = 1, q = 1, we can collect all the balls at a cost of 1 by collecting them in the order (1, 1), (2, 2).

Sample Input 2

3
1 4
4 6
7 8

Sample Output 2

1

If we choose p = -3, q = -2, we can collect all the balls at a cost of 1 by collecting them in the order (7, 8), (4, 6), (1, 4).

Sample Input 3

4
1 1
1 2
2 1
2 2

Sample Output 3

2

Solution in Java8 (OpenJDK 1.8.0) (judge verdict: Wrong Answer)

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.math.BigInteger;
 
public class Main implements Runnable {
	
	//static int mod = 1000000007;

    public static void main(String[] args) {
    	new Thread(null, new Main(), "", 16 * 1024 * 1024).start();
    }
    
    public void run() {
        FastScanner sc = new FastScanner();

        int n = sc.nextInt();
        int[] x = new int[n];
        int[] y = new int[n];
        
        for(int i=0;i<n;i++){
        	x[i] = sc.nextInt();
        	y[i] = sc.nextInt();
        }
        
        int maxnocost = 0;
        for(int i=0;i<n;i++){
        	for(int j=i+1;j<n;j++){
        		int nocost = 0;
        		HashSet<Kind> set = new HashSet<>();
        		int p = x[i] - x[j];
        		int q = y[i] - y[j];
        		
        		if(p==0 || q==0){
        			continue;
        		}
        		
        		for(int k=0;k<n;k++){
        			Kind nk = new Kind(mod(x[k],p),mod(y[k],q),x[k]/p - y[k]/q);
        			if(set.add(nk)){	//まだない

        			}
        			else{	//既にあった
        				nocost++;
        			}
        		}
        		
        		maxnocost =Math.max(maxnocost, nocost);
        	}
        }
        
    	int noc = 0;
    	HashSet<Integer> s = new HashSet<>();
    	for(int k=0;k<n;k++){
    		if(s.add(x[k])){
    			
    		}
    		else{
    			noc++;
    		}
    	}
		maxnocost = Math.max(maxnocost, noc);
		
    	noc = 0;
    	s = new HashSet<>();
    	for(int k=0;k<n;k++){
    		if(s.add(y[k])){
    			
    		}
    		else{
    			noc++;
    		}
    	}
		maxnocost = Math.max(maxnocost, noc);

        System.out.println(n-maxnocost);
    }
    
	static int mod(int a, int mod) {
		return (a % mod + mod) % mod;
	}
}

class Kind{
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + div;
		result = prime * result + modp;
		result = prime * result + modq;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Kind other = (Kind) obj;
		if (div != other.div)
			return false;
		if (modp != other.modp)
			return false;
		if (modq != other.modq)
			return false;
		return true;
	}

	int modp;
	int modq;
	int div;
	
	public Kind(int modp,int modq, int div){
		this.modp = modp;
		this.modq = modq;
		this.div = div;
	}
}

class FastScanner {
	private final InputStream in = System.in;
	private final byte[] buffer = new byte[1024];
	private int ptr = 0;
	private int buflen = 0;
	private boolean hasNextByte() {
		if (ptr < buflen) {
			return true;
		} else {
			ptr = 0;
			try {
				buflen = in.read(buffer);
			} catch (IOException e) {
				e.printStackTrace();
			}
			if (buflen <= 0) {
				return false;
			}
		}
		return true;
	}
	private int readByte() {
		if (hasNextByte())
			return buffer[ptr++];
		else
			return -1;
	}
	private static boolean isPrintableChar(int c) {
		return 33 <= c && c <= 126;
	}
	public boolean hasNext() {
		while (hasNextByte() && !isPrintableChar(buffer[ptr]))
			ptr++;
		return hasNextByte();
	}
	public String next() {
		if (!hasNext())
			throw new NoSuchElementException();
		StringBuilder sb = new StringBuilder();
		int b = readByte();
		while (isPrintableChar(b)) {
			sb.appendCodePoint(b);
			b = readByte();
		}
		return sb.toString();
	}
	public long nextLong() {
		if (!hasNext())
			throw new NoSuchElementException();
		long n = 0;
		boolean minus = false;
		int b = readByte();
		if (b == '-') {
			minus = true;
			b = readByte();
		}
		if (b < '0' || '9' < b) {
			throw new NumberFormatException();
		}
		while (true) {
			if ('0' <= b && b <= '9') {
				n *= 10;
				n += b - '0';
			} else if (b == -1 || !isPrintableChar(b)) {
				return minus ? -n : n;
			} else {
				throw new NumberFormatException();
			}
			b = readByte();
		}
	}
	public int nextInt() {
		long nl = nextLong();
		if (nl < Integer.MIN_VALUE || nl > Integer.MAX_VALUE)
			throw new NumberFormatException();
		return (int) nl;
	}
	public int[] nextIntArray(int n){
		int[] a = new int[n];
		for(int i=0;i<n;i++){
			a[i] = nextInt();
		}
		return a;
	}
	public Integer[] nextIntegerArray(int n){
		Integer[] a = new Integer[n];
		for(int i=0;i<n;i++){
			a[i] = nextInt();
		}
		return a;
	}
	public double nextDouble() {
		return Double.parseDouble(next());
	}
}