#include<queue>
#include<stdio.h>
#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
#include<string.h>

#ifdef LOCAL
#define eprintf(...) fprintf(stderr, __VA_ARGS__)
#else
#define NDEBUG
#define eprintf(...) do {} while (0)
#endif
#include<cassert>

using namespace std;

typedef long long LL;
typedef vector<int> VI;

#define REP(i,n) for(int i=0, i##_len=(n); i<i##_len; ++i)
#define EACH(i,c) for(__typeof((c).begin()) i=(c).begin(),i##_end=(c).end();i!=i##_end;++i)

template<class T> inline void amin(T &x, const T &y) { if (y<x) x=y; }
template<class T> inline void amax(T &x, const T &y) { if (x<y) x=y; }
template<class Iter> void rprintf(const char *fmt, Iter begin, Iter end) {
    for (bool sp=0; begin!=end; ++begin) { if (sp) putchar(' '); else sp = true; printf(fmt, *begin); }
    putchar('\n');
}
namespace MIN_COST_MAX_FLOW {
typedef LL Flow;
typedef LL Cost;
const Flow FLOW_INF = 1LL<<60;
const Cost COST_INF = 1LL<<60;

const int SIZE = 65;
vector<pair<Cost, int> > B[SIZE];
Cost last;
int sz;

int bsr(Cost c) {
    if (c == 0) return 0;
    return __lg(c)+1;
}

void init() {
    last = sz = 0;
    REP (i, SIZE) B[i].clear();
}

void push(Cost cst, int v) {
    assert(cst >= last);
    sz++;
    B[bsr(cst^last)].emplace_back(cst, v);
}

pair<Cost, int> pop_min() {
    assert(sz);
    if (B[0].empty()) {
	int k = 1;
	while (k < SIZE && B[k].empty()) k++;
	assert(k < SIZE);
	last = B[k][0].first;
	EACH (e, B[k]) amin(last, e->first);
	EACH (e, B[k]) B[bsr(e->first^last)].push_back(*e);
	B[k].clear();
    }
    assert(B[0].size());
    pair<Cost, int> ret = B[0].back();
    B[0].pop_back();
    sz--;
    return ret;
}

struct MinCostMaxFlow {
    struct Edge {
	int dst;
	Cost cst;
	Flow cap;
	int rev;
    };
    typedef vector<vector<Edge> > Graph;
    Graph G;
    vector<vector<pair<int, LL> > > H;
    MinCostMaxFlow(int N) : G(N), H(N) {}

    void add_edge(int u, int v, Cost x, Flow f) {
	if (u == v) return;
	H[u].emplace_back(v, x);
	G[u].push_back((Edge){ v, x, f, (int)G[v].size() });
	G[v].push_back((Edge){ u, -x, 0, (int)G[u].size()-1 });
    }

    Flow flow;
    Cost cost;
    Flow solve(int s, int t, Flow limit = FLOW_INF) {
	flow = 0;
	cost = 0;
	vector<Cost>len(G.size()), h(G.size(), COST_INF);
	{
	    VI in(G.size());
	    h[s] = 0;
	    in[s] = true;
	    queue<int> qu; qu.push(s);
	    while (!qu.empty()) {
		int v = qu.front(); qu.pop();
		in[v] = false;
		EACH (e, H[v]) {
		    int w = e->first;
		    if (h[w] > h[v] + e->second) {
			h[w] = h[v] + e->second;
			if (!in[w]) qu.push(w);
		    }
		}
	    }
	}
	vector<int> prev(G.size()), prev_num(G.size());
	while (limit > 0) {
	    init(); push(0, s);
	    fill(len.begin(), len.end(), COST_INF);
	    fill(prev.begin(), prev.end(), -1);
	    len[s] = 0;
	    while (sz) {
		pair<Cost, int> p = pop_min();
		Cost cst = p.first;
		int v = p.second;
		if (cst > len[v]) continue;
		for (int i=0; i<(int)G[v].size(); i++) {
		    const Edge &f = G[v][i];
		    Cost tmp = len[v] + f.cst + h[v] - h[f.dst];
		    if (f.cap > 0 && len[f.dst] > tmp) {
			len[f.dst] = tmp;
			push(tmp, f.dst);
			prev[f.dst] = v; prev_num[f.dst] = i;
		    }
		}
	    }

	    if (prev[t] == -1) return flow;
	    for (int i=0; i<(int)G.size(); i++) h[i] += len[i];
	    
	    Flow f = limit;
	    for (int v=t; v!=s; v=prev[v])
		f = min(f, G[prev[v]][prev_num[v]].cap);
	    for (int v=t; v!=s; v=prev[v]) {
		Edge &e = G[prev[v]][prev_num[v]];
		e.cap -= f;
		G[e.dst][e.rev].cap += f;
	    }
	    limit -= f;
	    flow += f;
	    cost += f * h[t];
	}
	return flow;
    }
};
}; // namespace MIN_COST_MAX_FLOW;
using MinCostMaxFlow = MIN_COST_MAX_FLOW::MinCostMaxFlow;

void UNIQUE(vector<LL> &v) {
    sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end()), v.end());
}

int N;
LL RX[1011], RY[1011];
LL BX[1011], BY[1011];
int RC[1011], BC[1011];

void MAIN() {
    scanf("%d", &N);
    REP (i, N) scanf("%lld%lld%d", RX+i, RY+i, RC+i);
    REP (i, N) scanf("%lld%lld%d", BX+i, BY+i, BC+i);
    int m = 0;
    REP (i, N) m += RC[i];

    vector<LL> sum, diff;

    REP (i, N) {
	sum.push_back(RX[i] + RY[i]);
	sum.push_back(BX[i] + BY[i]);
	diff.push_back(RX[i] - RY[i]);
	diff.push_back(BX[i] - BY[i]);
    }

    UNIQUE(sum);
    UNIQUE(diff);

    const int SUM_BASE = N*2;
    const int SUM_BASE_2 = SUM_BASE + sum.size();
    const int DIFF_BASE = SUM_BASE_2 + sum.size();
    const int DIFF_BASE_2 = DIFF_BASE + diff.size();
    const int SRC = DIFF_BASE_2 + diff.size();
    const int SNK = SRC+1;

    MinCostMaxFlow G(SNK+1);
    REP (i, N) {
	G.add_edge(SRC, i, 0, RC[i]);
	G.add_edge(i+N, SNK, 0, BC[i]);

	LL key;
	int pos;

	key = RX[i] + RY[i];
	pos = lower_bound(sum.begin(), sum.end(), key) - sum.begin();
	G.add_edge(i, pos + SUM_BASE, 0, RC[i]);
	G.add_edge(i, pos + SUM_BASE_2, 0, RC[i]);

	key = RX[i] - RY[i];
	pos = lower_bound(diff.begin(), diff.end(), key) - diff.begin();
	G.add_edge(i, pos + DIFF_BASE, 0, RC[i]);
	G.add_edge(i, pos + DIFF_BASE_2, 0, RC[i]);

	key = BX[i] + BY[i];
	pos = lower_bound(sum.begin(), sum.end(), key) - sum.begin();
	G.add_edge(pos + SUM_BASE, i+N, 0, BC[i]);
	G.add_edge(pos + SUM_BASE_2, i+N, 0, BC[i]);

	key = BX[i] - BY[i];
	pos = lower_bound(diff.begin(), diff.end(), key) - diff.begin();
	G.add_edge(pos + DIFF_BASE, i+N, 0, BC[i]);
	G.add_edge(pos + DIFF_BASE_2, i+N, 0, BC[i]);
    }

    REP (i, (int)sum.size() - 1) {
	G.add_edge(i+SUM_BASE, i+1+SUM_BASE, sum[i]-sum[i+1], 100000);
	G.add_edge(i+1+SUM_BASE_2, i+SUM_BASE_2, sum[i]-sum[i+1], 100000);
    }
    REP (i, (int)diff.size() - 1) {
	G.add_edge(i+DIFF_BASE, i+1+DIFF_BASE, diff[i]-diff[i+1], 100000);
	G.add_edge(i+1+DIFF_BASE_2, i+DIFF_BASE_2, diff[i]-diff[i+1], 100000);
    }

    G.solve(SRC, SNK);
    printf("%lld\n", -G.cost);
}

int main() {
    int TC = 1;
//    scanf("%d", &TC);
    REP (tc, TC) MAIN();
    return 0;
}



Measured memory_bytes: 5632