### BUGGY CODE ###

# coding: utf-8
import array, bisect, collections, copy, heapq, itertools, math, random, re, string, sys, time
sys.setrecursionlimit(10 ** 7)
INF = 10 ** 20
MOD = 10 ** 9 + 7


def II(): return int(input())
def ILI(): return list(map(int, input().split()))
def IAI(LINE): return [ILI() for __ in range(LINE)]
def IDI(): return {key: value for key, value in ILI()}


def read():
    N = II()
    x, y = [], []
    for __ in range(N):
        _x, _y = ILI()
        x.append(_x)
        y.append(_y)
    return N, x, y


AdjacentVertex = collections.namedtuple("AdjacentVertex", "vertex cost")
NO_VERTEX = -1


def compute_mst_prim(max_v, adj_list):
    # pred[u]は頂点uの「ひとつ前」の頂点を表す
    pred = collections.defaultdict(lambda: NO_VERTEX)
    # uとpred[u]を結ぶ辺の重みがkey[u]に入る
    key = collections.defaultdict(lambda: INF)
    key[0] = 0
    # 二分ヒープを優先度付きキューとして用いる
    pq = [(key[v], v) for v in range(max_v)]
    heapq.heapify(pq)
    # 優先度付きキューに頂点が入っているかを示す配列
    in_pq = array.array("B", (True for _ in range(max_v)))
    while pq:
        _, u = heapq.heappop(pq)
        in_pq[u] = False
        for v, v_cost in adj_list[u]:
            if in_pq[v]:
                weight = v_cost
                if weight < key[v]:
                    pred[v] = u
                    key[v] = weight
                    heapq.heappush(pq, (weight, v))
                    in_pq[v] = True
    return (pred, key)


def solve(N, x, y):
    edges = []
    for i in range(N):
        for j in range(i + 1, N):
            edges.append((min(abs(x[i] - x[j]), abs(y[i] - y[j])) , i, j))

    adjacency_list = collections.defaultdict(set)

    for ind, (w, s, t) in enumerate(edges):
        adjacency_list[s].add(AdjacentVertex(t, w))
        adjacency_list[t].add(AdjacentVertex(s, w))
    (_, key) = compute_mst_prim(N, adjacency_list)

    ans = sum(key.values())
    return ans


def main():
    params = read()
    print(solve(*params))


if __name__ == "__main__":
    main()


### FIXED CODE ###

# coding: utf-8
import array, bisect, collections, copy, heapq, itertools, math, random, re, string, sys, time

sys.setrecursionlimit(10 ** 7)
INF = 10 ** 20
MOD = 10 ** 9 + 7


def II(): return int(input())
def ILI(): return list(map(int, input().split()))
def IAI(LINE): return [ILI() for __ in range(LINE)]
def IDI(): return {key: value for key, value in ILI()}


def read():
    N = II()
    x, y = [], []
    for i in range(N):
        _x, _y = ILI()
        x.append((i, _x))
        y.append((i, _y))
    return N, x, y


class UnionFind(object):
    def __init__(self, number_of_nodes):  # 初期化
        self.par = array.array("L", range(number_of_nodes))
        self.rank = array.array("L", (0 for i in range(number_of_nodes)))

    def root(self, node):  # 根を求める
        if self.par[node] == node:
            return node
        else:
            r = self.root(self.par[node])
            self.par[node] = r  # 経路圧縮
            return r

    def in_the_same_set(self, node1, node2):  # 同じ集合に属するか
        return self.root(node1) == self.root(node2)

    def unite(self, node1, node2):  # 属する集合を併合
        x = self.root(node1)
        y = self.root(node2)
        if x == y:
            pass
        elif self.rank[x] < self.rank[y]:
            self.par[x] = y
        else:
            self.par[y] = x
            if self.rank[x] == self.rank[y]:
                self.rank[x] += 1


def solve(N, x, y):
    edges = []
    x.sort(key=lambda x: x[1])
    y.sort(key=lambda x: x[1])
    for i in range(N - 1):
        x_s, x_e = x[i], x[i + 1]
        y_s, y_e = y[i], y[i + 1]
        edges.append((x_s[0], x_e[0], x_e[1] - x_s[1]))
        edges.append((y_s[0], y_e[0], y_e[1] - y_s[1]))

    edges.sort(key=lambda x: x[2])
    union_find = UnionFind(N)
    ans = 0
    for edge in edges:
        if not union_find.in_the_same_set(edge[0], edge[1]):
            union_find.unite(edge[0], edge[1])
            ans += edge[2]

    return ans


def main():
    params = read()
    print(solve(*params))


if __name__ == "__main__":
    main()