#include <bits/stdc++.h>

#define REP(i, n) for(decltype(n) i = 0; i < n; i++)

#define ALL(c)   c.begin(), c.end()
#define SORT(c)  std::sort(ALL(c))
#define RSORT(c) std::sort(ALL(c), std::greater<decltype(c)::value_type>())

using namespace std;
using ll = long long;

const int MOD  = (int)1e9 + 7;
const int INF  = (int)1e9 + 1;
const ll  LINF = (ll)1e18 + 1;

class UnionFind {
    using ll = long long;
public:
    struct Node {
        ll parent, rank, weight;
        Node() = default;
        Node(const ll& _parent,
             const ll& _rank,
             const ll& _weight = 0) :
            parent(_parent),
            rank(_rank),
            weight(_weight) { }
    };

    std::vector<Node> node;

    UnionFind(const ll& num_node) :
        node(num_node) {
        for(ll i = 0; i < num_node; i++) {
            node[i].parent = i;
        }
    }

    ll searchAndZipRoot(const ll& x) {
        if(node[x].parent == x) {
            return x;
        }
        else {
            auto r = searchAndZipRoot(node[x].parent);
            node[x].weight += node[node[x].parent].weight;
            return node[x].parent = r;
        }
    }

    ll calcWeight(const ll& x) {
        searchAndZipRoot(x);
        return node[x].weight;
    }

    void unite(const ll& x,
               const ll& y,
               const ll& weight = 0) {
        auto rx = searchAndZipRoot(x);
        auto ry = searchAndZipRoot(y);
        if(rx != ry) {
            auto yw = weight + calcWeight(x) - calcWeight(y);
            if(node[rx].rank < node[ry].rank) {
                std::swap(rx, ry);
                yw *= -1;
            }
            node[ry].parent = rx;
            node[ry].weight = yw;
            if(node[rx].rank == node[ry].rank) {
                node[rx].rank++;
            }
        }
    }

    ll calcDiff(const ll& x,
                const ll& y) {
        return std::abs(calcWeight(x) - calcWeight(y));
    }

    template<class... A>
    bool checkWhetherRootIsSame(const A&... nodes) {
        auto node_list = std::initializer_list<ll>{nodes...};
        if(node_list.size() < 2) return false;
        auto v = searchAndZipRoot(*node_list.begin());
        for(size_t i = 1; i < node_list.size(); i++) {
            if(v != searchAndZipRoot(*(node_list.begin() + i))) return false;
        }
        return true;
    }
};

class CommonGraph {
    using ll = long long;
public:
    struct Edge {
        enum class Type { Directed, Undirected };
        int    src, dst;
        ll     cost, capacity, flow;
        size_t rev;
        Edge() = default;
        Edge(const int&    _src,
             const int&    _dst,
             const ll&     _cost     = 0,
             const ll&     _capacity = 0,
             const ll&     _frow     = 0,
             const size_t& _rev      = 0) :
            src(_src),
            dst(_dst),
            cost(_cost),
            capacity(_capacity),
            flow(_frow),
            rev(_rev) { }
    };
    friend std::ostream& operator <<(std::ostream& os, const Edge& obj) {
        os << "src : "  << obj.src      << ", "
           << "dst : "  << obj.dst      << ", "
           << "cost : " << obj.cost     << ", "
           << "cap : "  << obj.capacity << ", "
           << "flow : " << obj.flow     << ", "
           << "rev : "  << obj.rev;
        return os;
    }

    struct Vertex {
        ll cost;
        Vertex(const ll& _cost) : cost(_cost) { }
    };

    enum class Type { Normal, Flow };

    CommonGraph(const int&        num_vertex,
                const Type&       type,
                const Edge::Type& edge_type) :
        vertex_(num_vertex, 0),
        edge_(num_vertex),
        num_edge_(0),
        total_cost_(0),
        type_(type),
        edge_type_(edge_type) { }

    ll getTotalCost() const {
        return total_cost_;
    }

    /**
     *  辺を追加する
     */
    void addEdge(const int& src,
                 const int& dst,
                 const ll&  cost,
                 const ll&  capacity = 0) {
        switch(type_) {
            case Type::Normal: {
                switch(edge_type_) {
                    case Edge::Type::Directed: {
                        edge_[src].emplace_back(src, dst, cost);
                        break;
                    }
                    case Edge::Type::Undirected: {
                        edge_[src].emplace_back(src, dst, cost);
                        edge_[dst].emplace_back(dst, src, cost);
                        break;
                    }
                }
                break;
            }
            case Type::Flow: {
                switch(edge_type_) {
                    case Edge::Type::Directed: {
                        edge_[src].emplace_back(src, dst, cost,  capacity, 0, edge_[dst].size());
                        edge_[dst].emplace_back(dst, src, -cost,        0, 0, edge_[src].size() - 1);
                        break;
                    }
                    case Edge::Type::Undirected: {
                        edge_[src].emplace_back(src, dst, cost, capacity, 0, edge_[dst].size());
                        edge_[dst].emplace_back(dst, src, cost, capacity, 0, edge_[src].size() - 1);
                        break;
                    }
                }
                break;
            }
        }
        num_edge_++;
        total_cost_ += cost;
    }

    /**
     *  辺の情報を標準出力する
     */
    void dumpEdgeInfo() {
        for(const auto& v : edge_) {
            for(const auto& e : v) {
                std::cout << "[" << e << "]" << std::endl;
            }
            std::cout << std::endl;
        }
    }

    /**
     *  最大流問題を解く(Dinic法)
     *  @src_index:  始点
     *  @sink_index: 終点
     *  @Return:     最大流
     */
    ll calcMaxFlow(const int& src_index,
                   const int& sink_index) {
        if(type_ != Type::Flow) {
            throw std::logic_error("Graph type is invalid");
        }
        std::vector<int> level(vertex_.size()), iter(vertex_.size());
        std::function<int(void)> levelize
            = [&]() {
                  level.assign(vertex_.size(), -1);
                  level[src_index] = 0;
                  queue<int> Q; Q.push(src_index);
                  while(!Q.empty()) {
                      int u = Q.front(); Q.pop();
                      if(u == sink_index) break;
                      for(const auto& e : edge_[u]) {
                          if(e.flow < e.capacity && level[e.dst] < 0) {
                              Q.push(e.dst);
                              level[e.dst] = level[u] + 1;
                          }
                      }
                  }
                  return level[sink_index];
              };
        function<ll(int, ll)> augment
            = [&](int u, ll cur) {
                  if(u == sink_index) return cur;
                  for(size_t i = iter[u]; i < edge_[u].size(); ++i) {
                      auto& e = edge_[u][i];
                      auto& r = edge_[e.dst][e.rev];
                      if(e.flow < e.capacity && level[u] < level[e.dst]) {
                          auto f = augment(e.dst, min(cur, e.capacity - e.flow));
                          if(0 < f) { e.flow += f; r.flow -= f; return f; }
                      }
                  }
                  return 0LL;
              };
        for(auto& v : edge_) for(auto& e : v) e.flow = 0;
        auto flow = 0LL;
        while(0 <= levelize()) {
            std::fill(iter.begin(), iter.end(), 0);
            for(ll f; 0 < (f = augment(src_index, INF)); flow += f);
        }
        return flow;
    }

    /**
     *  頂点間の最小コストを求める(BFS)
     *  @min_cost:    最小コスト
     *  @start_index: 始点
     *  @goal_index:  終点
     *  @Return:      始点-終点が非連結の場合falseを返す
     */
    bool calcMinCostByBFS(ll&        min_cost,
                          const int& start_index,
                          const int& goal_index) {
        if(type_ != Type::Normal) {
            throw std::logic_error("Graph type is invalid");
        }
        std::fill(vertex_.begin(), vertex_.end(), std::numeric_limits<ll>::max());
        vertex_[start_index] = 0;
        queue<int> Q; Q.push(start_index);
        while(!Q.empty()) {
            const auto src = Q.front(); Q.pop();
            for(const auto& v : edge_[src]) {
                const auto& dst  = v.dst;
                const auto  cost = vertex_[src].cost + v.cost;
                if(cost < vertex_[dst].cost) {
                    vertex_[dst] = cost; Q.push(dst);
                }
            }
        }
        min_cost = vertex_[goal_index].cost;
        return (min_cost == std::numeric_limits<ll>::max()) ? false : true;
    }

    /**
     *  最小全域木を求める(Kruskal法)
     *  @Return: 最小全域木を返す
     *           グラフが非連結の場合、連結成分ごとに最小全域木を構成する
     */
    CommonGraph generateMinimumSpanningTree() {
        if(type_ != Type::Normal || edge_type_ != Edge::Type::Undirected) {
            throw std::logic_error("Graph type is invalid");
        }
        auto num_v = vertex_.size();
        CommonGraph mst(num_v, Type::Normal, Edge::Type::Undirected);
        std::vector<Edge> mst_e;
        mst_e.reserve(num_edge_ * 2);
        for(const auto& v : edge_) for(const auto& e : v) mst_e.push_back(e);
        auto sort_rule = [](const Edge& l, const Edge& r) { return l.cost < r.cost; };
        sort(mst_e.begin(), mst_e.end(), sort_rule);
        UnionFind uf(num_v);
        for(const auto& e : mst_e) {
            if(!uf.checkWhetherRootIsSame(e.src, e.dst)) {
                mst.addEdge(e.src, e.dst, e.cost);
                uf.unite(e.src, e.dst);
            }
        }
        return mst;
    }

    /**
     *  全頂点間の最短距離を求める(Warshall-Floyd法)
     *  @Return: array[i][j] :: 頂点iから頂点jまでの最短距離
     */
    std::vector<std::vector<ll>> calcMinCostBetweenAllVertices() {
        std::vector<std::vector<ll>> dp(vertex_.size(),
                                        std::vector<ll>(vertex_.size(),
                                                        std::numeric_limits<uint>::max()));
        for(const auto& v : edge_) for(const auto& e : v) dp[e.src][e.dst] = e.cost;
        for(size_t i = 0; i < vertex_.size(); i++)
            for(size_t j = 0; j < vertex_.size(); j++)
                for(size_t k = 0; k < vertex_.size(); k++)
                    dp[j][k] = std::min(dp[j][k], dp[j][i] + dp[i][k]);
        return dp;
    }

private:
    std::vector<Vertex>            vertex_;
    std::vector<std::vector<Edge>> edge_;
    ll                             num_edge_;
    ll                             total_cost_;
    const Type                     type_;
    const Edge::Type               edge_type_;
};

class Pt {
public:
    int x;
    int y;

    constexpr Pt() : x(0), y(0) { }
    constexpr Pt(const int& _x, const int& _y) : x(_x), y(_y) { }

    constexpr Pt(const Pt &obj) : x(obj.x), y(obj.y) { }
    Pt &operator=(const Pt &obj) { x = obj.x; y = obj.y; return *this; }

    std::vector<Pt> getAroundFourPts() const {
        std::vector<Pt> result;
        for(int hwi = -1; hwi <= 1; hwi++) {
            for(int wwi = -1; wwi <= 1; wwi++) {
                if((hwi == 0) ^ (wwi == 0)) {
                    result.emplace_back(x + wwi, y + hwi);
                }
            }
        }
        return result;
    }

    std::vector<Pt> getAroundPts() const {
        std::vector<Pt> result;
        for(int hwi = -1; hwi <= 1; hwi++) {
            for(int wwi = -1; wwi <= 1; wwi++) {
                if(hwi != 0 || wwi != 0) {
                    result.emplace_back(x + wwi, y + hwi);
                }
            }
        }
        return result;
    }

    constexpr bool isWithinRange(const int& w, const int& h) const {
        return 0 <= x && x < w && 0 <= y && y < h;
    }

    constexpr Pt operator +() const {
        return *this;
    }

    constexpr Pt operator -() const {
        return{ -x, -y };
    }

    constexpr Pt operator +(const Pt& other) const {
        return{ x + other.x, y + other.y };
    }

    constexpr Pt operator +=(const Pt& other) {
        x += other.x;  y += other.y;  return *this;
    }

    constexpr Pt operator -(const Pt& other) const {
        return{ x - other.x, y - other.y };
    }

    constexpr Pt operator -=(const Pt& other) {
        x -= other.x;  y -= other.y;  return *this;
    }

    constexpr Pt operator *(int s) const {
        return{ x * s, y * s };
    }

    constexpr Pt operator *=(const Pt& other) {
        x *= other.x;  y *= other.y;  return *this;
    }

    constexpr Pt operator /(int s) const {
        return{ x / s, y / s };
    }

    constexpr Pt operator /=(const Pt& other) {
        x /= other.x;  y /= other.y;  return *this;
    }

    constexpr bool operator ==(const Pt& other) {
        return x == other.x && y == other.y;
    }

    constexpr bool operator !=(const Pt& other) {
        return !(x == other.x && y == other.y);
    }

    friend std::ostream& operator <<(std::ostream& os, const Pt& obj) {
        os << "x : " << obj.x << ", "
           << "y : " << obj.y << std::endl;
        return os;
    }
};

template <typename T>
class MyMat {
public:
    using EleMap = std::map<T, std::vector<Pt>>;

    T* data;
    uint w, h;

    MyMat(const uint& _w, const uint& _h, const T& def_val = 0) :
        w(_w), h(_h) {
        data = new T[h * w]();
        if(def_val != 0) {
            for(uint i = 0; i < w * h; i++) {
                data[i] = def_val;
            }
        }
    }

    MyMat(const uint &_w, const uint &_h, const T*& _data) :
        w(_w), h(_h), data(_data) {}

    ~MyMat() { delete[] data; }

    MyMat(const MyMat &obj) : data(obj.data), w(obj.w), h(obj,h) {}
    MyMat &operator=(const MyMat &obj) { data = obj.data; w = obj.w; h = obj,h; return *this; }

    MyMat clone() {
        T* new_data = new T[h * w];
        for(int i = 0; i < h * w; i++) {
            new_data[i] = data[i];
        }

        return MyMat<T>(w, h, new_data);
    }

    void stdRead() {
        for(uint hi = 0; hi < h; hi++) {
            for(uint wi = 0; wi < w; wi++) {
                std::cin >> data[wi + hi * w];
            }
        }
    }

    template<class... A>
    EleMap searchElements(const A&... keys) const {
        auto key_list = std::initializer_list<T>{keys...};

        EleMap result;
        for(const auto& key : key_list) {
            result.insert(std::make_pair(key, std::vector<Pt>()));
        }

        for(uint hi = 0; hi < h; hi++) {
            for(uint wi = 0; wi < w; wi++) {
                for(const auto& key : key_list) {
                    if(data[wi + hi * w] == key) {
                        result[key].push_back(Pt(wi, hi));
                    }
                }
            }
        }
    }

    T& at(const uint& hi, const uint& wi) const {
        return data[wi + hi * w];
    }

    T& at(const Pt& pt) const {
        return data[pt.x + pt.y * w];
    }

    friend std::ostream& operator <<(std::ostream& os, const MyMat<T>& obj) {
        for(uint hi = 0; hi < obj.h; hi++) {
            for (uint wi = 0; wi < obj.w; wi++) {
                os << obj.data[wi + hi * obj.w] << " ";
            }
            os << std::endl;
        }
        return os;
    }
};

int main() {
    try {
        cin.tie(0);
        ios::sync_with_stdio(false);

        int H, W;
        std::cin >> H >> W;

        CommonGraph g(10,
                      CommonGraph::Type::Normal,
                      CommonGraph::Edge::Type::Directed);

        REP(i, 10) {
            REP(j, 10) {
                int c;
                std::cin >> c;
                g.addEdge(i, j, c);
            }
        }

        MyMat<int> mat(W, H);
        mat.stdRead();

        auto dp  = g.calcMinCostBetweenAllVertices();
        auto ans = 0;
        REP(y, H) {
            REP(x, W) {
                if(mat.at(y, x) == -1 || mat.at(y, x) == 1) {
                    continue;
                }
                else {
                    ans += dp[mat.at(y, x)][1];
                }
            }
        }

        std::cout << ans << std::endl;
    }
    catch(const exception& e) {
        std::cout << e.what() << std::endl;
        exit(1);
    }
    return 0;
}


Measured memory_bytes: 384