#![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 ::*;
	
	#[derive(Copy, Clone, Debug)]
	pub struct E {
		pub to: usize,
		pub cap: i64,
		pub init: i64,
		pub cost: i64,
		pub rev: usize
	}
	
	#[derive(Clone, Debug)]
	pub struct Graph {
		pub es: Vec<Vec<E>>,
		pub ex: Vec<i64>,
		pub p: Vec<i64>,
		iter: Vec<usize>
	}
	
	impl Graph {
		pub fn new(n: usize) -> Graph {
			Graph { es: vec![vec![]; n], ex: vec![0; n], p: vec![0; n], iter: vec![0; n] }
		}
		pub fn add(&mut self, v: usize, to: usize, cap: i64, cost: i64) {
			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: 0, init: 0, cost: -cost, rev: fwd });
		}
		fn is_admissible(&self, v: usize, e: &E) -> bool {
			e.cap > 0 && e.cost + self.p[v] - self.p[e.to] < 0
		}
		/// 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 i64.
		/// Dual: minimize \sum_v ex(v)p(v) + \sum_{uv} cap(e) max(0, -cost(uv) - p(u) + p(v)).
		/// 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 = 0;
			for v in &mut self.es {
				for e in v {
					e.cost *= n as i64;
					eps.setmax(e.cost);
				}
			}
			let mut stack = vec![];
			let mut visit = vec![false; n];
			let mut ok = self.ex.iter().all(|&ex| ex == 0);
			'refine: loop {
				eps = std::cmp::max(1, eps / 8);
				if ok && self.fitting() {
					break;
				}
				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 = 0;
						}
					}
				}
				loop {
					for v in 0..n {
						self.iter[v] = 0;
						if self.ex[v] > 0 {
							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 <= 0 || 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] > 0 {
							let ex = self.ex[v];
							let f = self.dfs(v, ex);
							if f == 0 { break }
							else { self.ex[v] -= f }
						}
					}
				}
				ok = true;
			}
			for v in &mut self.es {
				for e in v {
					e.cost /= n as i64;
				}
			}
			ok
		}
		fn dfs(&mut self, v: usize, f: i64) -> i64 {
			if self.ex[v] < 0 {
				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 > 0 {
						self.es[v][self.iter[v]].cap -= d;
						self.es[e.to][e.rev].cap += d;
						return d;
					}
				}
				self.iter[v] += 1;
			}
			0
		}
		fn fitting(&mut self) -> bool {
			let n = self.es.len();
			let mut d: Vec<i64> = self.p.iter().map(|&a| a / (n as i64)).collect(); // p must be non-positive.
			let mut d2: Vec<i64> = (0..n).map(|v| d[v] * (n as i64) - self.p[v] + 1).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 > 0 && { let tmp = d2[v] + e.cost + self.p[v] - self.p[e.to] + 1; d2[e.to].setmin(tmp) } {
						if fixed[e.to] {
							return false;
						}
						d[e.to] = d[v] + e.cost / (n as i64);
						que.push((-d2[e.to], e.to));
					}
				}
			}
			self.p = d;
			true
		}
		pub fn val(&self) -> i64 {
			let mut tot = 0;
			for v in &self.es {
				for e in v {
					if e.cap < e.init {
						tot += (e.init - e.cap) * e.cost;
					}
				}
			}
			tot
		}
	}
}

fn run() {
	let N = read!(usize);
	let R = read!(i64, i64, i64; N);
	let B = read!(i64, i64, i64; N);
	let mut g = mcf::Graph::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());
}


Measured memory_bytes: 100732