#include <bits/stdc++.h>
#include <chrono>
using namespace std;
typedef long long ll;
#define tr(container, it) \
    for (auto it = container.begin(); it != container.end(); it++)
#define scontains(c, x) ((c).find(x) != (c).end())   //O(log n)
#define contains(c, x) (find((c).begin(),(c).end(),x) != (c).end()) //O(n)
#define ill(_x)  ll _x;scanf("%lld",&_x);
#define idb(_x)  double _x;scanf("%lf",&_x);
#define pll pair<ll,ll>
#define pii pair<int,int>
#define mll map<ll,ll>
#define vll vector<ll>
#define sll set<ll>
#define vs vector<string>
#define in0(x, a, b)((x)>=a && (x)<=b    )
#define in1(x, a, b)((x)>a && (x)<b)
#define  rep(i, begin, end) for (__typeof(end) i = (begin) - ((begin) > (end)); i != (end) - ((begin) > (end)); i += 1 - 2 * ((begin) > (end)))
const double pi = 3.14159265358979323846;
const double e  = 2.71828182845904523536;
const int INF = 0x3f3f3f3f;
const int MOD = (int) (1e9) + 7;

//cout << "Plz enter the number of bits" <<endl;
//cin >> size;
//
//freopen("/home/joduskame/Desktop/cp/IO/Output.txt", "w", stdout);
//cout << setw(max(3,size))<<"num"<<"  two_c" <<" one_c" <<endl;
//int show_index=MX-size-1;
//cout << setw(max(3,size))<<-(int)pow(2,size-1)<< ": " << (bitset<MX>(-(int)pow(2,size-1)).to_string()).substr(show_index,size)<<" Not exist"<<"\n";
//for (int i = -(int)pow(2,size-1)+1; i < (int)pow(2,size-1); ++i) {
//bitset<MX> bt=i<0? bitset<MX>((int)(pow(2,size)-1) ^(-i)):bitset<MX>(i);
//cout << setw(max(3,size))<<i<< ": " << (bitset<MX>(i).to_string()).substr(show_index,size)<<" "<<bt.to_string().substr(show_index,size)<<"\n";
//}

// Binary search (note boundaries in the caller)
int CeilIndex(std::vector<int> &v, int l, int r, int key) {
    while (r-l > 1) {
        int m = l + (r-l)/2;
        if (v[m] >= key)
            r = m;
        else
            l = m;
    }
    vector<int> a;
    a.clear();

    return r;
}

int LongestIncreasingSubsequenceLength(std::vector<int> &v) {
    if (v.size() == 0)
        return 0;

    std::vector<int> tail(v.size(), 0);
    int length = 1; // always points empty slot in tail

    tail[0] = v[0];
    for (size_t i = 1; i < v.size(); i++) {
        if (v[i] < tail[0])
            // new smallest value
            tail[0] = v[i];
        else if (v[i] > tail[length-1])
            // v[i] extends largest subsequence
            tail[length++] = v[i];
        else
            // v[i] will become end candidate of an existing subsequence or
            // Throw away larger elements in all LIS, to make room for upcoming grater elements than v[i]
            // (and also, v[i] would have already appeared in one of LIS, identify the location and replace it)
            tail[CeilIndex(tail, -1, length-1, v[i])] = v[i];
    }

    return length;
}

void swap2(int &a,int &b){
    a^=b^=a^=b;
}

int add(int x,int y){
    while(y){
        x=x^y;
        y=((x^y)&y)<<1;
    }
    return x;
}

int min2(int x, int y)
{
    return  y + ((x - y) & ((x - y) >> 31));
}

int min3(int a,int b,int c){
    int d=1;
    while (a-- && b-- &&c-- &&d++){}
    return d-1;

}
int D[30][30];
int C[500][500];
pll M0[30];
pll M1[30];
pll M2[30];

void _(){
    int n,c;
    cin >> n>> c;
    for (int i = 0; i < c; ++i) {
        for (int j = 0; j < c; ++j) {
            cin >> D[i][j];
        }
    }
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            cin >> C[i][j];
        }
    }

    for (int k = 0; k < c; ++k) {
        ll s0=0,s1=0,s2=0;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                if((i+j)%3==0){
                    s0+=D[C[i][j]-1][k];
                }
                else if((i+j)%3==1){
                    s1+=D[C[i][j]-1][k];
                }
                else{
                    s2+=D[C[i][j]-1][k];
                }
            }
        }
        M0[k]={s0,k};
        M1[k]={s1,k};
        M2[k]={s2,k};
    }
    sort(M0,M0+c);
    sort(M1,M1+c);
    sort(M2,M2+c);

    ll mi=LLONG_MAX;
    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 3; ++j) {
            for (int k = 0; k < 3; ++k) {
                if(M0[i].second!=M1[j].second && M1[j].second!=M2[k].second && M0[i].second!=M2[k].second){
                    mi=min(M0[i].first+M1[j].first+M2[k].first,mi);
                }
            }
        }
    }
    cout << mi;







    }


int main() {
#ifdef debug
    freopen("/home/joduskame/Desktop/cp/IO/Input.txt", "r", stdin);
    freopen("/home/joduskame/Desktop/cp/IO/Output.txt", "w", stdout);
#endif

    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    _();
    return 0;

}








Measured memory_bytes: 1280