Problem:
Score : 400 points

Problem Statement

On a two-dimensional plane, there are N red points and N blue points.
The coordinates of the i-th red point are (a_i, b_i), and the coordinates of the i-th blue point are (c_i, d_i).

A red point and a blue point can form a friendly pair when, the x-coordinate of the red point is smaller than that of the blue point, and the y-coordinate of the red point is also smaller than that of the blue point.

At most how many friendly pairs can you form? Note that a point cannot belong to multiple pairs.

Constraints

All input values are integers.

1 \leq N \leq 100

0 \leq a_i, b_i, c_i, d_i < 2N

a_1, a_2, ..., a_N, c_1, c_2, ..., c_N are all different.

b_1, b_2, ..., b_N, d_1, d_2, ..., d_N are all different.

Input

Input is given from Standard Input in the following format:

N
a_1 b_1
a_2 b_2
:
a_N b_N
c_1 d_1
c_2 d_2
:
c_N d_N

Output

Print the maximum number of friendly pairs.

Sample Input 1

3
2 0
3 1
1 3
4 2
0 4
5 5

Sample Output 1

2

For example, you can pair (2, 0) and (4, 2), then (3, 1) and (5, 5).

Sample Input 2

3
0 0
1 1
5 2
2 3
3 4
4 5

Sample Output 2

2

For example, you can pair (0, 0) and (2, 3), then (1, 1) and (3, 4).

Sample Input 3

2
2 2
3 3
0 0
1 1

Sample Output 3

0

It is possible that no pair can be formed.

Sample Input 4

5
0 0
7 3
2 2
4 8
1 6
8 5
6 9
5 4
9 1
3 7

Sample Output 4

5

Sample Input 5

5
0 0
1 1
5 5
6 6
7 7
2 2
3 3
4 4
8 8
9 9

Sample Output 5

4

Solution in C# (Mono 4.6.2.0) (judge verdict: Wrong Answer)

using System;
using System.Collections.Generic;
using System.Linq;

public struct Point : IComparable
{
    public int X;
    public int Y;

    public Point(Point p)
    {
        X = p.X;
        Y = p.Y;
    }

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }

    public static bool operator ==(Point a, Point b)
    {
        return (a.X == b.X && a.Y == b.Y);
    }

    public static bool operator !=(Point a, Point b)
    {
        return !(a == b);
    }

    //objと自分自身が等価のときはtrueを返す
    public override bool Equals(object obj)
    {
        if (!(obj is Point))
        {
            return false;
        }

        Point p = (Point)obj;
        return (X == p.X && Y == p.Y);
    }

    //Equalsがtrueを返すときに同じ値を返す
    public override int GetHashCode()
    {
        return (X << 16) ^ Y;
    }

    public Point Clone()
    {
        return new Point(X, Y);
    }

    public void Reverse()
    {
        X = -X;
        Y = -Y;
    }

    public static Point operator +(Point a, Point b)
    {
        return new Point(a.X + b.X, a.Y + b.Y);
    }

    public static Point operator -(Point a, Point b)
    {
        return new Point(a.X - b.X, a.Y - b.Y);
    }

    public static Point operator *(Point a, int i)
    {
        return new Point(a.X * i, a.Y * i);
    }

    public static Point operator /(Point a, int i)
    {
        return new Point(a.X / i, a.Y / i);
    }
    public static Point operator +(Point a)
    {
        return new Point(+a.X, +a.Y);
    }

    public static Point operator -(Point a)
    {
        return new Point(-a.X, -a.Y);
    }

    public override string ToString()
    {
        return string.Format("({0},{1})", X, Y);
    }

    public int CompareTo(object obj)
    {
        var that = (Point)obj;
        if (X - that.X != 0)
        {
            return X - that.X;
        }
        return Y - that.Y;
    }
}

public class MultiDictionary<TKey, TValue>
{
    private readonly Dictionary<TKey, HashSet<TValue>> mDictionary = new Dictionary<TKey, HashSet<TValue>>();

    /// <summary>
    /// 指定したキーに関連付けられている複数の値を取得または設定します
    /// </summary>
    public HashSet<TValue> this[TKey key]
    {
        get { return mDictionary[key]; }
        set { mDictionary[key] = value; }
    }

    /// <summary>
    /// キーを格納しているコレクションを取得します
    /// </summary>
    public Dictionary<TKey, HashSet<TValue>>.KeyCollection Keys
    {
        get { return mDictionary.Keys; }
    }

    /// <summary>
    /// 複数の値を格納しているコレクションを取得します
    /// </summary>
    public Dictionary<TKey, HashSet<TValue>>.ValueCollection Values
    {
        get { return mDictionary.Values; }
    }

    /// <summary>
    /// 格納されているキーと値のペアの数を取得します
    /// </summary>
    public int Count
    {
        get { return mDictionary.Count; }
    }

    /// <summary>
    /// 指定したキーと値をディクショナリに追加します
    /// </summary>
    public void Add(TKey key, TValue value)
    {
        HashSet<TValue> set;
        if (!mDictionary.TryGetValue(key, out set))
        {
            set = new HashSet<TValue>();
            mDictionary[key] = set;
        }
        set.Add(value);
    }

    /// <summary>
    /// 指定したキーと複数の値をディクショナリに追加します
    /// </summary>
    public void Add(TKey key, params TValue[] values)
    {
        foreach (var n in values)
        {
            Add(key, n);
        }
    }

    /// <summary>
    /// 指定したキーと複数の値をディクショナリに追加します
    /// </summary>
    public void Add(TKey key, IEnumerable<TValue> values)
    {
        foreach (var n in values)
        {
            Add(key, n);
        }
    }

    /// <summary>
    /// 指定したキーを持つ値を削除します
    /// </summary>
    public bool Remove(TKey key, TValue value)
    {
        return mDictionary[key].Remove(value);
    }

    /// <summary>
    /// 指定したキーを持つ複数の値を削除します
    /// </summary>
    public bool Remove(TKey key)
    {
        return mDictionary.Remove(key);
    }

    /// <summary>
    /// すべてのキーと複数の値を削除します
    /// </summary>
    public void Clear()
    {
        mDictionary.Clear();
    }

    /// <summary>
    /// 指定したキーと値が格納されているかどうかを判断します
    /// </summary>
    public bool Contains(TKey key, TValue value)
    {
        return mDictionary[key].Contains(value);
    }

    /// <summary>
    /// 指定したキーが格納されているかどうかを判断します
    /// </summary>
    public bool ContainsKey(TKey key)
    {
        return mDictionary.ContainsKey(key);
    }

    /// <summary>
    /// 反復処理する列挙子を返します
    /// </summary>
    public IEnumerator<KeyValuePair<TKey, HashSet<TValue>>> GetEnumerator()
    {
        foreach (var n in mDictionary)
        {
            yield return n;
        }
    }

    public IEnumerable<KeyValuePair<TKey, TValue>> GetFlatten()
    {
        foreach (var n in mDictionary)
        {
            foreach (var v in n.Value)
            {
                yield return new KeyValuePair<TKey, TValue>(n.Key, v);
            }
        }
    }

    // TKey, TValue自体はColneしない
    public MultiDictionary<TKey, TValue> Clone()
    {
        var mdict = new MultiDictionary<TKey, TValue>();
        foreach (var kv in this)
        {
            mdict[kv.Key] = new HashSet<TValue>(kv.Value);
        }
        return mdict;
    }
}

class Solution
{
    static List<int> SearchWay(MultiDictionary<int, int> redToBlue, MultiDictionary<int, int> blueToRed, Dictionary<int, int> matchRedToBlue, Dictionary<int, int> matchBlueToRed, int start, int end)
    {
        return SearchWay(redToBlue, blueToRed, matchRedToBlue, matchBlueToRed, new List<int> { start }, end);
    }

    static List<int> SearchWay(MultiDictionary<int, int> redToBlue, MultiDictionary<int, int> blueToRed, Dictionary<int, int> matchRedToBlue, Dictionary<int, int> matchBlueToRed, List<int> path, int dest, bool currentIsRed = true)
    {
        List<int> result = null;
        var current = path.Last();
        Console.Error.WriteLine($"search {string.Join(" ", path)}");
        if (current == dest && !currentIsRed)
        {
            result = path.ToList();
            result.Add(dest);
            return result;
        }

        // 非マッチングWayを進む
        if (currentIsRed)
        {
            foreach (var next in redToBlue[current].Where(v => !path.Contains(v)))
            {
                Console.Error.WriteLine($"nonmatch to {next}");
                path.Add(next);
                result = SearchWay(redToBlue, blueToRed, matchRedToBlue, matchBlueToRed, path, dest, false);
                path.RemoveAt(path.Count - 1);
                if (result != null)
                {
                    break;
                }
            }
            return result;
        }
        // マッチングWayを進む
        else
        {
            int next;
            if (matchBlueToRed.TryGetValue(current, out next) && !path.Contains(next))
            {
                Console.Error.WriteLine($"match to {next}");
                path.Add(next);
                result = SearchWay(redToBlue, blueToRed, matchRedToBlue, matchBlueToRed, path, dest, true);
                path.RemoveAt(path.Count - 1);
            }
            return result;
        }
    }

    static void Main()
    {
        int n = int.Parse(Console.ReadLine());
        var reds = new Point[n];
        var blues = new Point[n];
        for (int i = 0; i < n; i++)
        {
            var vals = Console.ReadLine().Split(' ').Select(int.Parse).ToArray();
            reds[i] = new Point(vals[0], vals[1]);
        }
        for (int i = 0; i < n; i++)
        {
            var vals = Console.ReadLine().Split(' ').Select(int.Parse).ToArray();
            blues[i] = new Point(vals[0], vals[1]);
        }

        // redは0~n-1,blueはn~2n-1で番号を割当
        var redToBlue = new MultiDictionary<int, int>();
        var blueToRed = new MultiDictionary<int, int>();
        for (int i = 0; i < n; i++)
        {
            redToBlue[i] = new HashSet<int>();
        }
        for (int i = n; i < n * 2; i++)
        {
            blueToRed[i] = new HashSet<int>();
        }

        for (int i = 0; i < n; i++)
        {
            var red = reds[i];
            for (int j = 0; j < n; j++)
            {
                var blue = blues[j];
                if (red.CompareTo(blue) < 0)
                {
                    redToBlue.Add(i, j + n);
                    blueToRed.Add(j + n, i);
                    Console.Error.WriteLine($"way {i}->{j + n}");
                }
            }
        }

        var matchRedToBlue = new Dictionary<int, int>();
        var matchBlueToRed = new Dictionary<int, int>();

        // 増加道を探す
        bool newWay = true;
        while (newWay)
        {
            newWay = false;
            foreach (var i in Enumerable.Range(0, n).Where(v => !matchRedToBlue.ContainsKey(v)))
            {
                if (!redToBlue.ContainsKey(i))
                {
                    continue;
                }
                foreach (var j in redToBlue[i].Where(v => !matchBlueToRed.ContainsKey(v)))
                {
                    Console.Error.WriteLine($"try:{i}->{j}");
                    var way = SearchWay(redToBlue, blueToRed, matchRedToBlue, matchBlueToRed, i, j);
                    if (way != null)
                    {
                        Console.Error.WriteLine($"findto:{i}->{j}");
                        Console.Error.WriteLine($"newWay:{string.Join("->", way)}");
                        newWay = true;
                        // 新マッチングで上書き
                        for (int k = 0; k < way.Count - 1; k += 2)
                        {
                            matchRedToBlue[way[k]] = way[k + 1];
                            matchBlueToRed[way[k + 1]] = way[k];
                        }
                        foreach (var kv in matchRedToBlue)
                        {
                            Console.Error.WriteLine($"{kv.Key}->{kv.Value}");
                        }
                        break;
                    }
                }
            }
        }

        Console.WriteLine(matchRedToBlue.Count);
    }
}