import std.stdio;
import std.ascii;
import std.range;
import std.array;
import std.functional;
import std.algorithm;
import std.conv;
import std.container;
import std.math;
import std.numeric;
import std.string;
import std.c.string;
import std.random;
import std.regex;
import std.typecons;

const INF = int.max / 4;

class PrimalDual {
    struct P {
        int v, cost;
    }

    class Edge {
        int from, to, cap, cost;
        this() {}
        this(int from, int to, int cap, int cost) {
            this.from = from;
            this.to = to;
            this.cap = cap;
            this.cost = cost;
        }
        Edge rev;
        override string toString() {
            return "Edge " ~ [from, to, cap, cost].to!string;
        }
    }

    int V;
    Edge[][] G;
    int[] H;
    int[] dist;
    Edge[] preve;
    void addEdge(int from, int to, int cap, int cost) {
        //writeln([from, to, cap, cost]);
        auto a = new Edge(from, to, cap, cost),
             b = new Edge(to, from, 0, -cost);
        a.rev = b;
        b.rev = a;
        G[from] ~= a;
        G[to] ~= b;
    }
    this(int V) {
        this.V = V;
        this.G = new Edge[][V];
        this.H = new int[V];
        this.dist = new int[V];
        this.preve = new Edge[V];
    }

    int min_cost_flow(int s, int t, int flow) {
        int ret = 0;
        H[] = 0;
        auto used = new bool[V];
        while (flow > 0) {
            //writeln([s, t, flow]);
            dist[] = INF;
            dist[s] = 0;
            BinaryHeap!(Array!P, "a.cost > b.cost") PQ;
            PQ.insert(P(s, 0));
            memset(used.ptr, false, used.sizeof);
            while (!PQ.empty) {
                auto p = PQ.front; PQ.removeFront;
                used[p.v] = false;
                if (dist[p.v] < p.cost) continue;
                foreach (e; G[p.v]) {
                    if (e.cap > 0 && dist[e.to] > dist[p.v] + e.cost + H[p.v] - H[e.to]) {
                        dist[e.to] = dist[p.v] + e.cost + H[p.v] - H[e.to];
                        preve[e.to] = e;
                        if (used[e.to]) continue;
                        used[e.to] = true;
                        PQ.insert(P(e.to, dist[e.to]));
                    }
                }
            }
            if (dist[t] == INF) return -1;
            foreach (v; 0 .. V) H[v] += dist[v];
            int d = flow;
            for (int v = t; v != s; v = preve[v].from) {
                d = min(d, preve[v].cap);
            }
            flow -= d;
            ret += d * H[t];
            for (int v = t; v != s; v = preve[v].from) {
                auto e = preve[v];
                e.cap -= d;
                e.rev.cap += d;
            }
        }
        return ret;
    }
}

struct R {
    int a, b;
}

void main() {
    int N; scanf("%d\n", &N);
    auto P = new R[N];
    long S = 0;
    foreach (i; 0 .. N) {
        int a, b; scanf("%d %d\n", &a, &b);
        P[i].a = a; P[i].b = b;
        S += a + b;
    }
    auto G = new PrimalDual(3 * N + 2);
    for (int i = 1; i <= N; i++) {
        G.addEdge(0, i, 1, 0);
    }
    int[int] id;
    int cid = N + 1;
    for (int i = 1; i <= N; i++) {
        auto p = P[i - 1];
        if (p.a !in id) { id[p.a] = cid++; }
        if (p.b !in id) { id[p.b] = cid++; }
        G.addEdge(i, id[p.a], 1, p.a);
        G.addEdge(i, id[p.b], 1, p.b);
        G.addEdge(i, 3 * N + 1, 1, p.a + p.b);
    }
    for (int i = N + 1; i < cid; i++) {
        G.addEdge(i, 3 * N + 1, 1, 0);
    }
    writeln(S - G.min_cost_flow(0, 3 * N + 1, N));
}

Measured memory_bytes: 2504