unittest
{
	assert( [ "2 3", "0 1 1", "1 0 1", "1 4 0", "1 2", "3 3" ].solve == 3 );
	assert( [ "4 3", "0 12 71", "81 0 53", "14 92 0", "1 1 2 1", "2 1 1 2", "2 2 1 3", "1 1 2 2" ].solve == 428 );
	
	assert( [ "2 4", "100 100 100 100", "100 0 1 1", "100 1 0 1", "100 1 4 0", "2 3", "4 4" ].solve == 3 );		// 追加ケース
}



import std.algorithm;
import std.conv;
import std.format;
import std.range;
import std.stdio;

void main()
{
	stdin.byLineCopy.solve.writeln;
}

auto solve( Range )( Range input )
if( isInputRange!Range && is( ElementType!Range == string ) )
{
	auto n = 0L;
	auto c = 0L;
	input.front.formattedRead!"%s %s"( n, c );
	auto dss = new long[][]( c, c );
	foreach( ref ds; dss )
	{
		input.popFront;
		ds = input.front.split.to!( long[] );
	}
	auto css = new long[][]( n, n );
	foreach( ref cs; css )
	{
		input.popFront;
		cs = input.front.split.to!( long[] );
		cs[] -= 1;
	}
	
	// 同色にしなければいけないマスごとに塗り替えた後の違和感の合計を計算
	auto ts = new long[][]( 3, c );
	foreach( x; 0L .. n )
	{
		foreach( y; 0L .. n )
		{
			foreach( z; 0L .. c )
			{
				ts[ ( x + y ) % 3 ][ z ] += dss[ css[ x ][ y ] ][ z ];
			}
		}
	}
	
	// 違和感の合計の最小を計算
	auto ans = long.max;
	foreach( i; 0L .. c )
	{
		foreach( j; 0L .. c )
		{
			if( i == j ) continue;
			foreach( k; 0L .. c )
			{
				if( i == k || j == k ) continue;
				ans = min( ans, ts[ 0 ][ i ] + ts[ 1 ][ j ] + ts[ 2 ][ k ] );
			}
		}
	}
	return ans;
}
