#include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


struct Info{
	int x,y,z;
};

int N,K,S;
int adj_num[2000],adj_index[2000][2],lost_S[2000][2000];
int boss[2000],height[2000],group_min_adj[2000];
bool used[2000];
Info info[2000];

//自分のボスのindexを取得しつつ、経路圧縮を行う関数
int get_boss(int id){
	if(boss[id] == id)return id; //自分が代表なら、自分の値を返す
	else{
		return boss[id] = get_boss(boss[id]); //代表でないなら、自分が所属する組織の代表を返しつつ、経路圧縮
	}
}

void unite(int x,int y){
	int boss_x = get_boss(x);
	int boss_y = get_boss(y);

	//既に同じグループなら何もしない
	if(boss_x == boss_y)return;

	//高さが高い方に吸収する
	if(height[x] > height[y]){

		boss[boss_y] = boss_x;

	}else if(height[x] < height[y]){

		boss[boss_x] = boss_y;

	}else{ //height[x] == height[y]

		boss[boss_y] = boss_x;
		height[x]++;
	}
}


void func(){

	for(int i = 0; i < N; i++){
		adj_num[i] = 0;
	}

	for(int i = 0; i < N; i++){
		scanf("%d %d %d",&info[i].x,&info[i].y,&info[i].z);
	}

	if(K == 1){
		printf("%d\n",6*S*S);
		return;
	}

	for(int i = 0; i < N; i++){
		boss[i] = i;
		height[i] = 0;
	}

	int diff_x,diff_y,diff_z;

	for(int i = 0; i < N-1; i++){
		for(int k = i+1; k < N; k++){

			if(info[i].x+S <= info[k].x || info[k].x+S <= info[i].x)continue;
			if(info[i].y+S <= info[k].y || info[k].y+S <= info[i].y)continue;
			if(info[i].z+S <= info[k].z || info[k].z+S <= info[i].z)continue;

			unite(i,k);

			adj_index[i][adj_num[i]++] = k;
			adj_index[k][adj_num[k]++] = i;

			diff_x = S-abs(info[i].x-info[k].x);
			diff_y = S-abs(info[i].y-info[k].y);
			diff_z = S-abs(info[i].z-info[k].z);

			lost_S[i][k] = lost_S[k][i] = 2*(diff_x*diff_y+diff_x*diff_z+diff_y*diff_z);
		}
	}

	//各連結グループ内の、最小連結数を求める(輪構造対策)
	for(int i = 0; i < N; i++)group_min_adj[i] = BIG_NUM;
	int boss_id;
	for(int i = 0; i < N; i++){
		boss_id = get_boss(i);
		group_min_adj[boss_id] = min(group_min_adj[boss_id],adj_num[i]);
	}

	int ans = BIG_NUM,tmp,last_cube,next_cube,remove_cube;
	queue<int> Q;

	bool FLG;

	for(int i = 0; i < N; i++){
		boss_id = get_boss(i);
		if((group_min_adj[boss_id] != 2 && adj_num[i] == 2))continue; //輪対策

		while(!Q.empty())Q.pop();
		for(int i = 0; i < N; i++)used[i] = false; //★★輪は重なりの都合で全探索が必要★★

		//孤立、または連結の端の立方体
		tmp = 6*S*S;
		used[i] = true;
		last_cube = i;
		Q.push(i);

		while(!Q.empty()){
			if(Q.size() == K){ //連がK個になった場合

				FLG = false; //★★ちょうどK個の輪ができる場合あり!!★★

				if(group_min_adj[boss_id] == 2 && K >= 3){ //輪の一部の場合
					for(int k = 0; k < adj_num[last_cube]; k++){
						if(adj_index[last_cube][k] == Q.front()){
							FLG = true;
							break;
						}
					}
				}

				if(FLG){
					ans = min(ans,tmp-lost_S[last_cube][Q.front()]);
				}else{
					ans = min(ans,tmp);
				}


				next_cube = -1;
				for(int k = 0; k < adj_num[last_cube]; k++){
					if(used[adj_index[last_cube][k]])continue;
					next_cube = adj_index[last_cube][k];
				}
				if(next_cube == -1)break;
				used[next_cube] = true;

				//一番古い立方体を削除
				remove_cube = Q.front();
				Q.pop();

				tmp += 6*S*S-lost_S[last_cube][next_cube];
				tmp -= 6*S*S-lost_S[remove_cube][Q.front()];

				last_cube = next_cube;
				Q.push(last_cube);

			}else{

				next_cube = -1;
				for(int k = 0; k < adj_num[last_cube]; k++){
					if(used[adj_index[last_cube][k]])continue;
					next_cube = adj_index[last_cube][k];
				}
				if(next_cube == -1)break;
				used[next_cube] = true;

				tmp += 6*S*S-lost_S[last_cube][next_cube];

				last_cube = next_cube;
				Q.push(last_cube);
			}
		}
	}

	if(ans == BIG_NUM){
		printf("-1\n");
	}else{
		printf("%d\n",ans);
	}
}

int main(){

	while(true){
		scanf("%d %d %d",&N,&K,&S);
		if(N == 0 && K == 0 && S == 0)break;

		func();
	}

	return 0;
}

