#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>

using namespace std;

const int MAX_V = 10000;

struct edge{
  int to,cap,cost,rev;
};

vector<edge> G[MAX_V];
int dist[MAX_V];
int h[MAX_V]; // ポテンシャル

// 直前の頂点と辺
int prevv[MAX_V];
int preve[MAX_V];

const int INF = 1000000000;
// firstは最短距離、secondは頂点の番号
typedef pair<int,int> P;
// fromからtoへむかう容量cap,コストcostの辺をグラフに追加する。
void add_edge(int from,int to,int cap,int cost){	
  edge e;
  e.to = to,e.cap = cap,e.cost = cost,e.rev = G[to].size();
  G[from].push_back(e);

  e.to = from,e.cap = 0,e.cost = -cost,e.rev = G[from].size()-1;
  G[to].push_back(e);
}

// sからtへの流量fの最小費用流を求める
// ながせない場合は-1を返す
int min_cost_flow(int s,int t,int f,int V){
  int res = 0;
  fill(h,h+V,0);
  // 流量が限界に達するまで流す
  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;
      for(int i = 0; i < G[v].size(); i++){
	edge &e = G[v][i];
	if(e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]){
	  dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
	  prevv[e.to] = v;
	  preve[e.to] = i;
	  que.push(P(dist[e.to],e.to));
	}
      }
    }
    if(dist[t] == INF)
      return -1;
    for(int i = 0; i < V; i++)
      h[i] += dist[i];
    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;
}

int n;
int W[101][101];
int E[101][101];
bool field[101][101];
bool fin[101][101];

int main(){
  cin>>n;
  for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
      cin>>W[i][j];
  for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
      cin>>E[i][j];
  for(int i=0;i<n;i++)
    for(int j=0;j<n;j++){
      char ch;
      cin>>ch;
      if(ch=='o')field[i][j]=true;
      else field[i][j]=false;
    }
  for(int i=0;i<n;i++){
    for(int j=0;j<n;j++){
      int cost=0;
      for(int k=0;k<n;k++){
	if(k!=j){
	  if(field[i][k])
	    cost+=E[i][k];
	}
      }
      for(int k=0;k<n;k++){
	if(k!=i){
	  if(field[k][j])
	    cost+=E[k][j];
	}
      }
      if(!field[i][j])cost+=2*W[i][j];
      add_edge(i,j+n,1,cost);
    }
  }
  const int s=2*n;
  const int t=2*n+1;
  for(int i=0;i<n;i++)add_edge(s,i,1,0);
  for(int i=0;i<n;i++)add_edge(i+n,t,1,0);
  int a=min_cost_flow(s,t,n,2*n+2);
  cout<<a/2<<endl;
  for(int i=0;i<n;i++){
    for(int j=0;j<(int)G[i].size();j++){
      edge &e=G[i][j];
      if(e.to>=n&&e.to<2*n){
	if(e.cap==0)fin[i][e.to-n]=true;
	else fin[i][e.to-n]=false;
      }
    }
  }
  int cnt=0;
  for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
      if(field[i][j]^fin[i][j])cnt++;
  cout<<cnt<<endl;
  for(int i=0;i<n;i++){
    for(int j=0;j<n;j++){
      if(field[i][j]&&!fin[i][j])
	cout<<i+1<<" "<<j+1<<" erase"<<endl;
      else if(!field[i][j]&&fin[i][j])
	cout<<i+1<<" "<<j+1<<" write"<<endl;
    }
  }
  return 0;
}