#include <bits/stdc++.h>
using namespace std;
#define FOR(i,k,n) for(int i = (k); i < (n); i++)
#define REP(i,n) FOR(i,0,n)
#define ALL(a) a.begin(), a.end()
#define MS(m,v) memset(m,v,sizeof(m))
#define D10 fixed<<setprecision(10)
typedef vector<int> vi;
typedef vector<string> vs;
typedef pair<int, int> pii;
typedef long long ll;
typedef long double ld;
const int MOD = 1000000007;
//const int INF = MOD + 1;
const ld EPS = 1e-10;
template<class T> T &chmin(T &a, const T &b) { return a = min(a, b); }
template<class T> T &chmax(T &a, const T &b) { return a = max(a, b); }

/*--------------------template--------------------*/
#define MAX_V 10000
int V;

typedef int Weight;
const Weight INF = 1000000000;
// const Weight eps = 1e-8;

struct Edge {
	int src, dest;
	int cap, rev;
	Weight weight;
	bool operator < (const Edge &rhs) const { return weight > rhs.weight; }
};

typedef vector<Edge> Edges;
typedef vector<Edges> Graph;
typedef vector<Weight> Array;
typedef vector<Array> Matrix;

Weight h[MAX_V];                // potential
Weight dist[MAX_V];             // minimum distance
int prevv[MAX_V], preve[MAX_V]; // previous vertex and edge

void add_edge(Graph &g, int src, int dest, int cap, Weight weight) {
	g[src].push_back(Edge{ src, dest, cap, (int)g[dest].size(), weight });
	g[dest].push_back(Edge{ dest, src, 0, (int)g[src].size() - 1, -weight });
}

Weight min_cost_flow(Graph &g, int s, int t, int f) {
	Weight res = 0; V = g.size();
	memset(h, 0, sizeof(h));
	typedef pair<Weight, int> P;
	while (f > 0) {
		priority_queue<P, vector<P>, greater<P> > que;
		fill(dist, dist + V, INF);
		dist[s] = 0;
		que.push(P(0, s));
		while (!que.empty()) {
			P p = que.top(); que.pop();
			int v = p.second;
			if (dist[v] < p.first) continue;
			REP(i, g[v].size()) {
				Edge &e = g[v][i];
				if (e.cap > 0 && dist[e.dest] > dist[v] + e.weight + h[v] - h[e.dest] /* + eps */) {
					dist[e.dest] = dist[v] + e.weight + h[v] - h[e.dest];
					prevv[e.dest] = v;
					preve[e.dest] = i;
					que.push(P(dist[e.dest], e.dest));
				}
			}
		}
		if (dist[t] == INF) return -1;
		REP(v, V) h[v] += dist[v];

		int d = f;
		for (int v = t; v != s; v = prevv[v]) d = min(d, g[prevv[v]][preve[v]].cap);
		f -= d;
		res += d * h[t];
		for (int v = t; v != s; v = prevv[v]) {
			Edge &e = g[prevv[v]][preve[v]];
			e.cap -= d;
			g[v][e.rev].cap += d;
		}
	}
	return res;
}

struct op { int x, y, z; };

int main()
{
	int n;
	cin >> n;
	Matrix erase(n), write(n);
	REP(i, n)REP(j, n)
	{
		int a; cin >> a;
		write[i].push_back(a);
	}
	REP(i, n)REP(j, n)
	{
		int a; cin >> a;
		erase[i].push_back(a);
	}
	vs fld(n);
	REP(i, n) cin >> fld[i];
	Graph g(2*n + 2);
	REP(i, n)REP(j, n)
	{
		Weight w = 0;
		REP(k, n)
		{
			if (k == j) w += (fld[i][k] == 'o' ? 0 : write[i][k]);
			else w+= (fld[i][k] == '.' ? 0 : erase[i][k]);
		}
		add_edge(g, i, n+j, 1, w);
	}
	REP(i, n)
	{
		add_edge(g, 2 * n, i, 1, 0);
		add_edge(g, n + i, 2 * n + 1, 1, 0);
	}
	int ans = min_cost_flow(g, 2 * n, 2 * n + 1, n);
	cout << ans << endl;
	vi v;
	REP(i, n)
	{
		REP(j, g[i].size())
		{
			if (g[i][j].cap == 0) v.push_back(j);
		}
	}
	vector<op> ansv;
	REP(i, n)
	{
		REP(j, n)
		{
			if (v[i] == j&&fld[i][j] == '.') ansv.push_back(op{ i, j, 0 });
			else if (v[i] != j&&fld[i][j] == 'o') ansv.push_back(op{ i, j, 1 });
		}
	}
	cout << ansv.size() << endl;
	REP(i, ansv.size())
	{
		cout << ansv[i].x + 1 << " " << ansv[i].y + 1 << " " << (ansv[i].z == 0 ? "write\n" : "erase\n");
	}
	return 0;
}

Measured memory_bytes: 3700