Problem:
D: Dangerous Tower

ICPC で良い成績を収めるには修行が欠かせない．うさぎは ICPC で勝ちたいので，今日も修行をすることにした．

今日の修行は，積み木を丁寧に積み上げることによって，決してミスタイピングをしないほどの手先の器用さを手に入れようというものである．せっかく積み木がたくさんあるので，高い塔を作ろう．

積み木は N 個あり，i 番目 (1 ≤ i ≤ N) の積み木は 1 × Ai × Bi の直方体の形をしている．長さ 1 の辺は奥行き方向に用いて，長さ Ai, Bi の辺を横方向と高さ方向に 1 つずつ割り当てることにした．積み木を積み上げるとき，上の段の積み木は下の段の積み木より横の長さが厳密に短くなければならない．積み木は好きな順番に使うことができ，また，使わない積み木があってもよい．このような制約下で，作ることが可能な最も高い塔を作りたい．

Input

N
A1 B1
...
AN BN

1 ≤ N ≤ 1,000，1 ≤ Ai，Bi ≤ 1,000,000 を満たす．入力の値はすべて整数である．

Output

塔の高さの最大値を 1 行に出力せよ．

Sample Input 1

3
10 40
10 40
20 30

Sample Output 1

80

Sample Input 2

4
1 2
2 3
3 4
4 1

Sample Output 2

11

Solution in C++11 (judge verdict: Accepted)

#include <bits/stdc++.h>

using namespace std;

using ll = long long;
using Pi = pair<int, int>;
using Pl = pair<ll, ll>;
using Ti = tuple<int, int, int>;
using Tl = tuple<ll, ll, ll>;

#define pb push_back
#define eb emplace_back
#define mp make_pair
#define mt make_tuple
#define F first
#define S second
#define Get(t, i) get<(i)>((t))
#define all(v) (v).begin(), (v).end()
#define rep(i, n) for(int i = 0; i < (int)(n); i++)
#define reps(i, f, n) for(int i = (int)(f); i < (int)(n); i++)
#define each(a, b) for(auto& a : b)

const int inf = 1 << 25;
const ll INF = 1LL << 55;

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){}
};

struct PrimalDual
{
  vector< vector<edge> > G;
  vector<int> potential, mincost, prevv, preve;
  PrimalDual(int V):G(V), potential(V), mincost(V), prevv(V), preve(V){}
  void add_edge(int from, int to, int cap, int cost)
  {
    G[from].push_back(edge(to, cap, cost, (int)G[to].size()));
    G[to].push_back(edge(from, 0, -cost, (int)G[from].size()-1));
  }
  int min_cost_flow(int s, int t, int f)
  {
    int ret = 0;
    fill(potential.begin(), potential.end(), 0);
    fill(prevv.begin(), prevv.end(), -1);
    fill(preve.begin(), preve.end(), -1);

    while(f > 0) {
      priority_queue<Pi, vector<Pi>, greater<Pi> > que;
      fill(mincost.begin(), mincost.end(), inf);
      mincost[s] = 0;
      que.push(Pi(0, s));
      while(!que.empty()) {
	Pi p = que.top(); que.pop();
	int v = p.second;
	if(mincost[v] < p.first) continue;
	for(int i = 0; i < (int)G[v].size(); i++) {
	  edge& e = G[v][i];
	  int dual_cost = mincost[v] + e.cost + potential[v] - potential[e.to];
	  if(e.cap > 0 && dual_cost < mincost[e.to]) {
	    mincost[e.to] = dual_cost;
	    prevv[e.to] = v; preve[e.to] = i;
	    que.push(Pi(mincost[e.to], e.to));
	  }
	}
      }

      if(mincost[t] == inf) return ret; // -1;
      for(int v = 0; v < (int)G.size(); v++) potential[v] += mincost[v];
      int d = f;
      for(int v = t; v != s; v = prevv[v]) d = min(d, G[prevv[v]][preve[v]].cap);
      f -= d;
      ret += d * potential[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 ret;
  }
};

int main()
{
  int N; cin >> N;
  vector<int> A(N), B(N);
  vector<int> sides;
  rep(i, N) {
    cin >> A[i] >> B[i];
    sides.push_back(A[i]);
    sides.push_back(B[i]);
  }

  sort(all(sides));
  sides.erase(unique(all(sides)), sides.end());

  int dummy = N + sides.size(), s = dummy + 1, t = s + 1;
  PrimalDual flow(t + 1);
  for(int i = 0; i < N; i++) {
    int a = lower_bound(all(sides), A[i])-sides.begin();
    int b = lower_bound(all(sides), B[i])-sides.begin();

    flow.add_edge(s, i, 1, 0);
    flow.add_edge(i, dummy, 1, 0);
    flow.add_edge(i, N + a, 1, -B[i]);
    if(a != b) flow.add_edge(i, N + b, 1, -A[i]);
  }

  for(int i = 0; i < sides.size(); i++) flow.add_edge(N + i, t, 1, 0);
  flow.add_edge(dummy, t, inf, 0);

  cout << -flow.min_cost_flow(s, t, N) << endl; 
  
  return 0;
}