#include <algorithm>
#include <bitset>
#include <cassert>
#include <cctype>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <limits>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <stack>
#include <sstream>
#include <vector>
using namespace std;

#define REP2(i, m, n) for(int i = (int)(m); i < (int)(n); i++)
#define REP(i, n) REP2(i, 0, n)
#define ALL(c) (c).begin(), (c).end()
#define FOREACH(i, c) for(auto i = (c).begin(); i != (c).end(); ++i)
#define BIT(n, m) (((n) >> (m)) & 1)

typedef long long ll;
typedef pair<int, int> PI;

const ll inf = 1e15;
const ll mod = 1000 * 1000 * 1000 + 7;
const double eps = 1e-9;

template <typename S, typename T> ostream &operator<<(ostream &out, const pair<S, T> &p) {
  out << "(" << p.first << ", " << p.second << ")";
  return out;
}

template <typename T> ostream &operator<<(ostream &out, const vector<T> &v) {
  out << "[";
  REP(i, v.size()){
    if (i > 0) out << ", ";
    out << v[i];
  }
  out << "]";
  return out;
}


class MinimummCostFlow {
  struct Edge {
    int to, cap, cost, rev;
    Edge() {}
    Edge(int to, int cap, int cost, int rev) : to(to), cap(cap), cost(cost), rev(rev) {}
  };

  int V;
  static constexpr int INF = std::numeric_limits<int>::max() / 3;
  std::vector<std::vector<Edge> > G;
  std::vector<int> H;
  std::vector<int> D;
  std::vector<int> prevv, preve;

public:
  MinimummCostFlow(int V) : V(V), G(V), H(V), D(V), prevv(V), preve(V){}
  
  void AddEdge(int from, int to, int cap, int cost){
    assert(from != to);
    G[from].emplace_back(to, cap,  cost, G[to].size());
    G[to].emplace_back(from, 0, -cost, G[from].size() - 1);
  }
  
  int Solve(int s, int t, int f){
    int res = 0;
    fill(H.begin(), H.end(), 0);
    while (f > 0){
      typedef std::pair<int, int> P;
      std::priority_queue<P, std::vector<P>, std::greater<P> > Q;
      fill(D.begin(), D.end(), INF);

      D[s] = 0;
      Q.push(P(0, s));
      while (!Q.empty()){
        P p = Q.top(); Q.pop();
        int v = p.second;
        if (D[v] < p.first) continue;

        for (size_t i = 0; i < G[v].size(); i++){
          Edge &e = G[v][i];
          if (e.cap > 0 && D[e.to] > D[v] + e.cost + H[v] - H[e.to]){
            D[e.to] = D[v] + e.cost + H[v] - H[e.to];
            prevv[e.to] = v;
            preve[e.to] = i;
            Q.push(P(D[e.to], e.to));
          }
        }
      }

      if (D[t] == INF){
        return -1;        
      }

      for (int v = 0; v < V; v++){
        H[v] += D[v];
      }

      int d = f;
      for (int v = t; v != s; v = prevv[v]){
        d = std::min(d, G[prevv[v]][preve[v]].cap);
      }
      f -= d;
      
      res += d * H[t];
      for (int v = t; v != s; v= prevv[v]){
        Edge &e = G[prevv[v]][preve[v]];
        e.cap -= d;
        G[v][e.rev].cap += d;
      }
    }
    return res;
  }
};

int main(){
  int N;
  while (cin >> N && N){
    vector<int> A(N), B(N);
    REP(i, N) cin >> A[i] >> B[i];
    vector<int> U(N, false);
    vector<int> W;
    REP(i, N) W.push_back(A[i]);
    REP(i, N) W.push_back(B[i]);
    sort(ALL(W));
    W.erase(unique(ALL(W)), W.end());
    int M = W.size();

    int s = N + M;
    int t = s + 1;
    int f = N;
    int V = t + 1;
    MinimummCostFlow mcf(V);

    int res = 0;
    REP(i, N) res += max(A[i], B[i]);
    
    REP(i, N){
      mcf.AddEdge(s, i, 1, 0);
      mcf.AddEdge(i, t, 1, max(A[i], B[i]));
    }

    REP(i, N){
      int ja = N + lower_bound(ALL(W), A[i]) - W.begin();
      int jb = N + lower_bound(ALL(W), B[i]) - W.begin();
      mcf.AddEdge(i, ja, 1, max(A[i], B[i]) - B[i]);
      mcf.AddEdge(i, jb, 1, max(A[i], B[i]) - A[i]);
    }

    REP(i, W.size()){
      mcf.AddEdge(i + N, t, 1, 0);
    }
    cout << res - mcf.Solve(s, t, f) << endl;
  }
  return 0;
}

Measured memory_bytes: 1524