import std.stdio, std.string, std.conv;
import std.typecons;
import std.algorithm, std.array, std.range, std.container;
import std.math;

void main() {
	auto data = readln.split.to!(int[]);
	auto N = data[0], C = data[1];
	int[][] D, c;
	foreach (i; 0 .. C) { D ~= readln.split.to!(int[]); }
	foreach (i; 0 .. N) { c ~= readln.split.to!(int[]); }
	
	// mod_i[color] = (the number of grids that have the color)
	int[30] mod0, mod1, mod2;
	int mod0_n, mod1_n, mod2_n;
	foreach (x; 0 .. N) foreach(y; 0 .. N) {
		final switch ( (x+y)%3 ) {
			case 0: mod0[c[x][y]-1]++, mod0_n++; break;
			case 1: mod1[c[x][y]-1]++; mod1_n++; break;
			case 2: mod2[c[x][y]-1]++; mod2_n++; break;
		}
	}
	
	
	// the cost that is needed to re-paint
	int cost(int[30] mod, int color) {
		int result;
		foreach (i; 0 .. C) {
			result += mod[i] * D[i][color];
		}
		return result;
	}
	
	int solve(int[30] m0, int[30] m1, int[30] m2) {
		int result;
		auto m0s = C.iota.map!(x => cost(m0,x));
		auto index0 = C - m0s.minPos.length;
		auto m1s = C.iota.map!(x => x != index0 ? cost(m1,x) : int.max);
		auto index1 = C - m1s.minPos.length;
		auto m2s = C.iota.map!(x => x != index0 && x != index1 ? cost(m2,x) : int.max);
		auto index2 = C - m2s.minPos.length;
		
		return m0s[index0] + m1s[index1] + m2s[index2];
	}
	
	min(solve(mod0, mod1, mod2), solve(mod0, mod2, mod1), solve(mod1, mod0, mod2), solve(mod1, mod2, mod0), solve(mod2, mod0, mod1), solve(mod2, mod1, mod0)).writeln;
	
}