#![allow(non_snake_case, unused)]
use std::collections::*;

////////// missing functions in 1.15.1 //////////
macro_rules! eprint {
	($($t:tt)*) => {{
		use ::std::io::Write;
		let _ = write!(::std::io::stderr(), $($t)*);
	}};
}
macro_rules! eprintln {
	() => { eprintln!(""); };
	($($t:tt)*) => {{
		use ::std::io::Write;
		let _ = writeln!(::std::io::stderr(), $($t)*);
	}};
}
macro_rules! dbg {
	($v:expr) => {{
		let val = $v;
		eprintln!("[{}:{}] {} = {:?}", file!(), line!(), stringify!($v), val);
		val
	}}
}
/////////////////////////////////////////////////
pub trait SetMinMax {
	fn setmin(&mut self, v: Self) -> bool;
	fn setmax(&mut self, v: Self) -> bool;
}
impl<T> SetMinMax for T where T: PartialOrd {
	fn setmin(&mut self, v: T) -> bool {
		*self > v && { *self = v; true }
	}
	fn setmax(&mut self, v: T) -> bool {
		*self < v && { *self = v; true }
	}
}

macro_rules! mat {
	($($e:expr),*) => { Vec::from(vec![$($e),*]) };
	($($e:expr,)*) => { Vec::from(vec![$($e),*]) };
	($e:expr; $d:expr) => { Vec::from(vec![$e; $d]) };
	($e:expr; $d:expr $(; $ds:expr)+) => { Vec::from(vec![mat![$e $(; $ds)*]; $d]) };
}

macro_rules! ok {
	($a:ident$([$i:expr])*.$f:ident()$(@$t:ident)*) => {
		$a$([$i])*.$f($($t),*)
	};
	($a:ident$([$i:expr])*.$f:ident($e:expr$(,$es:expr)*)$(@$t:ident)*) => { {
		let t = $e;
		ok!($a$([$i])*.$f($($es),*)$(@$t)*@t)
	} };
}

pub fn readln() -> String {
	let mut line = String::new();
	::std::io::stdin().read_line(&mut line).unwrap_or_else(|e| panic!("{}", e));
	line
}

macro_rules! read {
	($($t:tt),*; $n:expr) => {{
		let stdin = ::std::io::stdin();
		let ret = ::std::io::BufRead::lines(stdin.lock()).take($n).map(|line| {
			let line = line.unwrap();
			let mut it = line.split_whitespace();
			_read!(it; $($t),*)
		}).collect::<Vec<_>>();
		ret
	}};
	($($t:tt),*) => {{
		let line = readln();
		let mut it = line.split_whitespace();
		_read!(it; $($t),*)
	}};
}

macro_rules! _read {
	($it:ident; [char]) => {
		_read!($it; String).chars().collect::<Vec<_>>()
	};
	($it:ident; [u8]) => {
		Vec::from(_read!($it; String).into_bytes())
	};
	($it:ident; usize1) => {
		$it.next().unwrap_or_else(|| panic!("input mismatch")).parse::<usize>().unwrap_or_else(|e| panic!("{}", e)) - 1
	};
	($it:ident; [usize1]) => {
		$it.map(|s| s.parse::<usize>().unwrap_or_else(|e| panic!("{}", e)) - 1).collect::<Vec<_>>()
	};
	($it:ident; [$t:ty]) => {
		$it.map(|s| s.parse::<$t>().unwrap_or_else(|e| panic!("{}", e))).collect::<Vec<_>>()
	};
	($it:ident; $t:ty) => {
		$it.next().unwrap_or_else(|| panic!("input mismatch")).parse::<$t>().unwrap_or_else(|e| panic!("{}", e))
	};
	($it:ident; $($t:tt),+) => {
		($(_read!($it; $t)),*)
	};
}

pub fn main() {
	let _ = ::std::thread::Builder::new().name("run".to_string()).stack_size(32 * 1024 * 1024).spawn(run).unwrap().join();
}

pub mod mcf {
	use ::*;
	use std::ops::*;
	
	type V = usize;
	
	#[derive(Copy, Clone, Debug)]
	pub struct E<C, W> {
		pub to: V,
		pub cap: C,
		pub init: C,
		pub cost: W,
		pub rev: usize
	}
	
	#[derive(Clone, Debug)]
	pub struct Graph<C, W> {
		pub es: Vec<Vec<E<C, W>>>,
		pub ex: Vec<C>,
		pub p: Vec<W>,
		iter: Vec<usize>
	}
	
	impl<C, W> Graph<C, W> where	C: Copy + Default + Ord + Sub<Output = C> + Neg<Output = C> + AddAssign + SubAssign,
									W: Copy + Default + Ord + Add<Output = W> + Sub<Output = W> + Mul<Output = W> + Div<Output = W> + Neg<Output = W>
															+ AddAssign + SubAssign + MulAssign + DivAssign + From<u32> {
		pub fn new(n: usize) -> Graph<C, W> {
			Graph { es: vec![vec![]; n], ex: vec![C::default(); n], p: vec![W::default(); n], iter: vec![0; n] }
		}
		pub fn add(&mut self, v: V, to: V, cap: C, cost: W) {
			let (fwd, rev) = (self.es[v].len(), self.es[to].len());
			self.es[v].push(E { to: to, cap: cap, init: cap, cost: cost, rev: rev });
			self.es[to].push(E { to: v, cap: C::default(), init: C::default(), cost: -cost, rev: fwd });
		}
		fn is_admissible(&self, v: V, e: &E<C, W>) -> bool {
			e.cap > C::default() && e.cost + self.p[v] - self.p[e.to] < W::default()
		}
		/// Compute minimum cost circulation.
		/// Return whether there is a flow satisfying the demand constraints.
		/// flow(e) = init(e) - cap(e).
		/// For solving min cost s-t flow of value F, set ex(s)=F and ex(t)=-F.
		/// For every vertex, the total capacity of its incident edges must be fit in C.
		/// C and W are expected to be i64 or Float (do not use f64).
		/// Dual: minimize \sum_v ex(v)p(v) + \sum_{uv} cap(e) max(0, -cost(uv) - p(u) + p(v)).
		/// To get the dual optimum, call fitting if W is integer, or use p/(n+1) if W is float.
		/// O(V^2 E log VC), where C=max(cost(e)). When cap=1, O(V E log VC).
		pub fn solve(&mut self) -> bool {
			let n = self.es.len();
			let mut eps = W::from(2);
			for v in &mut self.es {
				for e in v {
					e.cost *= (3 as u32 + 1).into();
					eps.setmax(e.cost);
				}
			}
			let mut stack = vec![];
			let mut visit = vec![false; n];
			let mut ok = false;
			'refine: while { eps /= 2.into(); eps > W::default() } {
				for v in 0..n {
					for i in 0..self.es[v].len() {
						let e = self.es[v][i];
						if self.is_admissible(v, &e) {
							self.ex[e.to] += e.cap;
							self.ex[v] -= e.cap;
							self.es[e.to][e.rev].cap += e.cap;
							self.es[v][i].cap = C::default();
						}
					}
				}
				loop {
					for v in 0..n {
						self.iter[v] = 0;
						if self.ex[v] > C::default() {
							visit[v] = true;
							stack.push(v);
						} else {
							visit[v] = false;
						}
					}
					if stack.len() == 0 { break }
					while let Some(v) = stack.pop() {
						for e in &self.es[v] {
							if !visit[e.to] && self.is_admissible(v, e) {
								visit[e.to] = true;
								stack.push(e.to);
							}
						}
					}
					if (0..n).filter(|&v| visit[v]).flat_map(|v| self.es[v].iter()).all(|e| e.cap <= C::default() || visit[e.to]) {
						assert!(!ok);
						break 'refine;
					}
					for v in (0..n).filter(|&v| visit[v]) { self.p[v] -= eps }
					for v in 0..n {
						while self.ex[v] > C::default() {
							let ex = self.ex[v];
							let f = self.dfs(v, ex);
							if f == C::default() { break }
							else { self.ex[v] -= f }
						}
					}
				}
				ok = true;
			}
			for v in &mut self.es {
				for e in v {
					e.cost /= (3 as u32 + 1).into();
				}
			}
			ok
		}
		fn dfs(&mut self, v: V, f: C) -> C {
			if self.ex[v] < C::default() {
				let d = ::std::cmp::min(f, -self.ex[v]);
				self.ex[v] += d;
				return d;
			}
			while self.iter[v] < self.es[v].len() {
				let e = self.es[v][self.iter[v]];
				if self.is_admissible(v, &e) {
					let d = self.dfs(e.to, ::std::cmp::min(f, e.cap));
					if d > C::default() {
						self.es[v][self.iter[v]].cap -= d;
						self.es[e.to][e.rev].cap += d;
						return d;
					}
				}
				self.iter[v] += 1;
			}
			C::default()
		}
		pub fn val<T>(&self) -> T where T: Default + From<C> + From<W> + AddAssign + Mul<Output = T> {
			let mut tot = T::default();
			for v in &self.es {
				for e in v {
					if e.cap < e.init {
						tot += T::from(e.init - e.cap) * e.cost.into();
					}
				}
			}
			tot
		}
		/// Find p s.t. cost(uv) + p(u) - p(v) >= 0 holds for every edge uv with cap(uv) > 0.
		/// W is expected to be i64. When W is float, use p/(n+1).
		/// O(E log V)
		pub fn fitting(&mut self) {
			let n = self.es.len();
			let mut d: Vec<W> = self.p.iter().map(|&a| a / (n as u32 + 1).into()).collect(); // p must be non-positive.
			let mut d2: Vec<W> = (0..n).map(|v| d[v] * (n as u32 + 1).into() - self.p[v] + 1.into()).collect();
			let mut fixed = vec![false; n];
			let mut que = ::std::collections::BinaryHeap::new();
			for v in 0..n {
				que.push((-d2[v], v))
			}
			while let Some((_, v)) = que.pop() {
				if fixed[v] { continue }
				fixed[v] = true;
				for e in &self.es[v] {
					if e.cap > C::default() && !fixed[e.to] && { let tmp = d2[v] + e.cost * (n as u32 + 1).into() + self.p[v] - self.p[e.to] + 1.into(); d2[e.to].setmin(tmp) } {
						d[e.to] = d[v] + e.cost;
						que.push((-d2[e.to], e.to));
					}
				}
			}
			self.p = d
		}
	}
}

fn run() {
	let N = read!(usize);
	let R = read!(i64, i64, i32; N);
	let B = read!(i64, i64, i32; N);
	let mut g = mcf::Graph::<i32, i64>::new(2 * N + 1);
	for i in 0..N {
		g.add(2 * N, i, R[i].2, 0);
		g.add(N + i, 2 * N, B[i].2, 0);
		for j in 0..N {
			g.add(i, N + j, 10, -(R[i].0 - B[j].0).abs() - (R[i].1 - B[j].1).abs());
		}
	}
	g.solve();
	println!("{}", -g.val::<i64>());
}


Measured memory_bytes: 84348