#[allow(unused_imports)]
use proconio::{input, marker::*};

#[allow(unused_imports)]
use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque};

#[allow(unused_imports)]
use std::io::Write;

#[allow(unused_macros)]
macro_rules! debug {
    ($($a:expr),*) => {
        #[cfg(debug_assertions)]
        writeln!(&mut std::io::stderr(), concat!("[DEBUG] ", $(stringify!($a), "={:?} "),*), $($a),*).unwrap();
    }
}

fn binary_search<F>(l: usize, r: usize, criterion: F) -> usize
where
    F: Fn(usize) -> bool,
{
    let mut l = l;
    let mut r = r;
    while r - l > 1 {
        let mid = l + (r - l) / 2;
        if criterion(mid) {
            r = mid;
        } else {
            l = mid;
        }
    }
    r
}

use std::cmp::max;
fn main() {
    input! {
        hl: usize,wl: usize, m: usize,
        hw: [(usize, usize);m],
    }
    println!("{}", solve(m, hw));
    // use rand::distributions::Uniform;
    // use rand::{thread_rng, Rng};

    // let hl = 5;
    // let wl = 5;
    // let mut rng = thread_rng();
    // let side = Uniform::new(1, hl);

    // let m = 5;
    // for _ in 0..100 {
    //     let mut hw = Vec::new();
    //     let mut set = BTreeSet::new();
    //     let mut count = 0;
    //     while count < m {
    //         let v = (rng.sample(side), rng.sample(side));
    //         if set.contains(&v) {
    //             continue;
    //         }
    //         set.insert(v);
    //         count += 1;
    //         hw.push(v);
    //     }
    //     assert_eq!(
    //         solve(m, hw.clone()),
    //         solve_st(hl, wl, m, hw.clone()),
    //         "{:?}",
    //         hw
    //     );
    // }
}

fn solve_st(hl: usize, wl: usize, m: usize, hw: Vec<(usize, usize)>) -> usize {
    let mut hmap = BTreeMap::new();
    let mut wmap = BTreeMap::new();
    let mut set = BTreeSet::new();
    for &(h, w) in &hw {
        *hmap.entry(h).or_insert(0usize) += 1;
        *wmap.entry(w).or_insert(0usize) += 1;
        set.insert((h, w));
    }

    let mut mx = 0;
    for i in 1..hl + 1 {
        for j in 1..wl + 1 {
            let hv = hmap.get(&i).unwrap_or(&0);
            let wv = wmap.get(&j).unwrap_or(&0);
            let v = hv + wv - if set.contains(&(i, j)) { 1 } else { 0 };
            mx = max(mx, v);
        }
    }
    mx
}

fn solve(m: usize, hw: Vec<(usize, usize)>) -> usize {
    let mut hmap = BTreeMap::new();
    let mut wmap = BTreeMap::new();
    let mut set = BTreeSet::new();
    for &(h, w) in &hw {
        *hmap.entry(h).or_insert(0usize) += 1;
        *wmap.entry(w).or_insert(0usize) += 1;
        set.insert((h, w));
    }
    let mut hvec = hmap.into_iter().collect::<Vec<_>>();
    let mut wvec = wmap.into_iter().collect::<Vec<_>>();
    hvec.sort_by_key(|e| e.1);
    wvec.sort_by_key(|e| e.1);

    let crit = |v: usize| {
        for &(h, hv) in hvec.iter().rev() {
            if hv + wvec[wvec.len() - 1].1 < v {
                break;
            }
            for &(w, wv) in wvec.iter().rev() {
                let vt = wv + hv - if set.contains(&(h, w)) { 1 } else { 0 };
                if vt >= v {
                    return false;
                } else if vt + 1 < v {
                    break;
                }
            }
        }
        true
    };

    return binary_search(0, m + 1, crit) - 1;
}


Measured memory_bytes: 46376