#[allow(unused_macros)]
macro_rules ! input { ( source = $ s : expr , $ ( $ r : tt ) * ) => { let mut iter = $ s . split_whitespace ( ) ; let mut next = || { iter . next ( ) . unwrap ( ) } ; input_inner ! { next , $ ( $ r ) * } } ; ( $ ( $ r : tt ) * ) => { let stdin = std :: io :: stdin ( ) ; let mut bytes = std :: io :: Read :: bytes ( std :: io :: BufReader :: new ( stdin . lock ( ) ) ) ; let mut next = move || -> String { bytes . by_ref ( ) . map ( | r | r . unwrap ( ) as char ) . skip_while ( | c | c . is_whitespace ( ) ) . take_while ( | c |! c . is_whitespace ( ) ) . collect ( ) } ; input_inner ! { next , $ ( $ r ) * } } ; }
#[allow(unused_macros)]
macro_rules ! input_inner { ( $ next : expr ) => { } ; ( $ next : expr , ) => { } ; ( $ next : expr , $ var : ident : $ t : tt $ ( $ r : tt ) * ) => { let $ var = read_value ! ( $ next , $ t ) ; input_inner ! { $ next $ ( $ r ) * } } ; }
#[allow(unused_macros)]
macro_rules ! read_value { ( $ next : expr , ( $ ( $ t : tt ) ,* ) ) => { ( $ ( read_value ! ( $ next , $ t ) ) ,* ) } ; ( $ next : expr , [ $ t : tt ; $ len : expr ] ) => { ( 0 ..$ len ) . map ( | _ | read_value ! ( $ next , $ t ) ) . collect ::< Vec < _ >> ( ) } ; ( $ next : expr , chars ) => { read_value ! ( $ next , String ) . chars ( ) . collect ::< Vec < char >> ( ) } ; ( $ next : expr , usize1 ) => { read_value ! ( $ next , usize ) - 1 } ; ( $ next : expr , $ t : ty ) => { $ next ( ) . parse ::<$ t > ( ) . expect ( "Parse error" ) } ; }

fn main() {
    input! {
        n: usize,
        poso: [(i64, i64); n]
    }
    let mut pos = poso;
    pos.sort();
    let mut hmap = std::collections::HashMap::new();
    for mask in bit_fixed_size_subsets(2, n) {
        let p = bit_filter(mask, &pos);
        let pre = p[0];
        let next = p[1];
        let delta = (next.0 - pre.0, next.1 - pre.1);
        *hmap.entry(delta).or_insert(0) += 1;
    }
    let mut cnt = 0;
    let mut p = (0,0);
    for (&k, &v) in hmap.iter() {
        if cnt < v && k != (0,0) {
            cnt = v;
            p = k;
        }
    }
    let mut cur = (1i64<<60,1i64<<60);
    let mut cost = 0;
    let mut rest = pos.iter().cloned() .into_iter().collect::<std::collections::BTreeSet<_>>();
    while rest.len() > 0 {
        let mut is_found = false;
        let restc = rest.clone();
        for b in restc {
            if add(cur, p) == b {
                cur = b;
                rest.remove(&b);
                is_found = true;
                break;
            }
        }
        if !is_found {
            cost += 1;
            cur = *rest.iter().next().unwrap();
            rest.remove(&cur);
        }
    }
    println!("{}", cost);
}

fn add(p: (i64,i64), q: (i64,i64)) -> (i64,i64) {
    (p.0+q.0,p.1+q.1)
}

#[allow(dead_code)]
pub fn bit_filter<T: Clone>(mask: usize, v: &[T]) -> Vec<T> {
    let mut ret = vec![];
    for i in 0..v.len() {
        if (mask >> i) & 1 == 1 {
            ret.push(v[i].clone());
        }
    }
    ret
}

#[allow(dead_code)]
fn bit_fixed_size_subsets(k: usize, n: usize) -> Vec<usize> {
    let mut ret = vec![];
    let mut x: i64 = (1 << k as i64) - 1;
    while x < 1 << n {
        ret.push(x as usize);
        if k == 0 {
            break;
        }
        let t = x | (x - 1);
        x = (t + 1) | (((!t & -!t) - 1) >> (x.trailing_zeros() + 1));
    }
    ret
}

Measured memory_bytes: 4352