#include <iostream>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <stack>
#include <list>
#include <utility>
#include <vector>
#include <set>
#include <algorithm>
#include <cmath>
#include <climits>
#include <map>
#include <unordered_map>
#include <string.h>
#include<iomanip>
#include <iterator>
#include <locale>

#define all(a) a.begin(),a.end()
#define VV(T) vector<vector< T > >
#define rep(i,N) for(int i = 0; i <N; i++)
#define repsg(i,s,g) for(int i = s; i <g; i++)
#define name(x) #x
using namespace std;

const int INF = INT_MAX;
const long long INFL = 1LL<<40;
const double PI = atan(1)*4;
//const int MOD = pow(10,9)+7;
int dy[]={0, 0, 1, -1, 0};
int dx[]={1, -1, 0, 0, 0};

typedef long long ll,LL;
typedef vector<int> vint;
typedef vector<vector<int> > vvint;
typedef vector<long long> vll, vLL;
typedef vector<vector<long long> > vvll, vvLL;
typedef pair<int,int> pii;
typedef pair<LL, LL> pLL;


// vector入力
template<typename T>
istream& operator >> (istream& is, vector<T>& vec){
    for(T& x: vec) is >> x;
    return is;
}
// vector出力
template<typename T>
ostream& operator << (ostream& os, vector<T>& vec){
    for(int i=0; i<vec.size(); i++){
        os << vec[i] << ( i+1 == vec.size() ? "" : " " );
    }
    return os;
}

struct UnionFind {
    // UnionFind u(n) 宣言
    // u.unionSet(x,y) 併合
    // u.findSet(x,y) -> {true, false}
    // u.root(x) xのroot. data[x]を参照。併合前はxそのもの、併合後は同一union
    vector<int> data;
    UnionFind(int size) : data(size, -1) { }
    bool unionSet(int x, int y) {
        x = root(x); y = root(y);
        if (x != y) {
            if (data[y] < data[x]) swap(x, y);
            data[x] += data[y];
            data[y] = x;
        }
        return x != y;
    }
    bool findSet(int x, int y) {
        return root(x) == root(y);
    }
    int root(int x) {
        return data[x] < 0 ? x : data[x] = root(data[x]);
    }
    int size(int x) {
        return -data[root(x)];
    }
};

int main() {
    int n;cin>>n;
    int c;cin>>c;
    vector<vector<int>> D(c);
    rep(i,c){
        D[i].resize(c);
        cin>>D[i];
    }
    
    vector<vector<int>> C(n);
    rep(i,n){
        C[i].resize(n);
        cin>>C[i];
    }
    
    vector<vector<int>> cnt(3);
    
    vector<int> sites(3,0);
    rep(i,3){
        cnt[i].resize(c,0);
    }
    
    rep(i,n){
        rep(j,n){
            cnt[(i+j)%3][C[i][j]-1]++;
            sites[(i+j)%3]++;
        }
    }
    
    
    int ans = INF;

    rep(i,c){
        repsg(j,i+1,c){
            repsg(k,j+1,c){
                vector<int> cols;cols.push_back(i);cols.push_back(j);cols.push_back(k);
                
                do{
                    int v = 0;
                    rep(m,3){
                        
                        //cols[m]に塗る。
                        
                        rep(s,c){
                            
                            v += (cnt[m][s])* D[s][cols[m]] ;
                            
                        }
                        
                        
                    }
                    
                    ans = min(ans,v);
                    
                    
                }while(next_permutation(all(cols)));
                
                
            }
        }
    }
    
    cout<<ans<<endl;
    
    return 0;
}


Measured memory_bytes: 1280