#include <iostream>
#include <vector>
#include <queue>
#include <tuple>

#define MAX (int)(1e8)

using namespace std;

int main() {
	int R, C;

	cin >> R >> C;

	vector<vector<int>> A(R, vector<int>(C - 1)), B(R - 1, vector<int>(C));
	vector<vector<vector<int>>> costs(2, vector<vector<int>>(R, vector<int>(C, 0)));
	vector<vector<vector<bool>>> visited(2, vector<vector<bool>>(R, vector<bool>(C, false)));

	for (int i = 0; i < R; i++) for (int j = 0; j < C - 1; j++)
		cin >> A[i][j];

	for (int i = 0; i < R - 1; i++) for (int j = 0; j < C; j++)
		cin >> B[i][j];

	//優先順位付きキューを使ってダイクストラ法を行う
	priority_queue<tuple<int, int, int, int>, vector<tuple<int, int, int, int>>, greater<tuple<int, int, int, int>>> que;

	//始点を追加
	que.push(make_tuple(0, 0, 0, 0));

	while (!que.empty()) {
		auto pos = que.top();
		int cost = get<0>(pos), f = get<1>(pos), r = get<2>(pos), c = get<3>(pos);
		que.pop();

		if (visited[f][r][c]) continue; //確定なら終了

		//コストを確定
		costs[f][r][c] = cost;
		visited[f][r][c] = true;

		//自分が下っている最中かどうかで分岐
		if (f == 0) {//下っていない
			//上下左右に移動を考える
			if (r + 1 < R)  //上
				que.push(make_tuple(cost + B[r][c], f, r + 1, c));
			if (c - 1 >= 0)	//左
				que.push(make_tuple(cost + A[r][c - 1], f, r, c - 1));
			if (c + 1 < C)	//右
				que.push(make_tuple(cost + A[r][c], f, r, c + 1));
			if (r - 1 >= 0) //下
				que.push(make_tuple(cost + 1, 1, r, c));
		}
		else {	//下っている
			//一つ下に移動するver or　下るのをやめる
			if (r - 1 >= 0)//下る
				que.push(make_tuple(cost + 1, f, r - 1, c));
			
			que.push(make_tuple(cost, 0, r, c)); //戻る

		}
	}

	cout << costs[0][R - 1][C - 1] << endl;

	return 0;
}
#include <bits/stdc++.h>
using namespace std;

#define rep(i, a, b) for(int i = (a); i < (b); ++i)
#define per(i, a, b) for(int i = (b)-1; i >= (a); --i)
#define all(x) begin(x), end(x)
#define sz(x) (int)(x).size()
#define pb push_back
#define eb emplace_back
#define mp make_pair
#define fst first
#define snd second

template<class T> bool ckmin(T& a, const T& b) { return b < a ? a = b, 1 : 0; }
template<class T> bool ckmax(T& a, const T& b) { return a < b ? a = b, 1 : 0; }

typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef vector<int> vi;
typedef vector<vi> vvi;
typedef vector<ll> vl;
typedef vector<vl> vvl;
typedef vector<pii> vii;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

map<int, int> rev;
const int N = 102;
const int M = 102102;
vi vis(N), ans(M), ok(M);
vii ed(M);

void dfs(vector<vii> &gr, int u, int p) {
  vis[u] = 1;
  for (auto [v, e]: gr[u]) if (p != v) {
    if (vis[v]) {
      ok[rev[e]] = 1;
      ans[rev[e]] = ed[rev[e]].fst == v;
    }
    else {
      ans[rev[e]] = ed[rev[e]].snd == v;
      ok[rev[e]] = 1;
      dfs(gr, v, u);
    }
  }
}

int main() {
  cin.tie(0)->sync_with_stdio(0);
  cin.exceptions(cin.failbit);

  int n, m;
  cin >> n >> m;

  vi c(n+1), done(m), vis(n+1);

  vector<vii> g(n+1);
  rep(i,0,m) {
    cin >> ed[i].first >> ed[i].second;
    g[ed[i].fst].eb(ed[i].snd, i);
    g[ed[i].snd].eb(ed[i].fst, i);
  }

  rep(i,1,n+1) cin >> c[i];

  rep(i,1,n+1) {
    if (vis[i]) continue;
    queue<int> q;
    q.push(i);
    vis[i] = 1;
    vi comp;
    while (!q.empty()) {
      int u = q.front(); q.pop();
      comp.pb(u);
      for (auto [v, j]: g[u]) if (!vis[v] && c[v] == c[u]) {
        vis[v] = 1;
        q.push(v);
      }
    }

    vector<vii> gr(n+1);
    int nedges = 0;
    for (auto u: comp) {
      for (auto [v, j]: g[u]) {
        if (c[u] == c[v] && !done[j]) {
          rev[nedges] = j;
          gr[u].eb(v, nedges);
          gr[v].eb(u, nedges);
          ++nedges;
          done[j] = 1;
        }
      }
    }

    dfs(gr, i, i);
  }

  rep(i,0,m) {
    if (!ok[i]) {
      assert(c[ed[i].fst] != c[ed[i].snd]);
      ans[i] = c[ed[i].fst] > c[ed[i].snd];
    }
    if (ans[i]) cout << "->";
    else cout << "<-";
    cout << '\n';
  }

}