//最小生成树,kruskal算法
//基础入门
#include<algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 105
struct Edge{//结构体表示边,其中包括两个点和权重
int x,y,w;
const bool operator<(const Edge& rhs)const{
return w<rhs.w;//重载比较运算符,对w从小到大
}
}edge[maxn];//数组相当边
int fa[maxn];//放顶点
int n,m;
int ans;
int find(int a){
if(fa[a]==a){
return a;
}
else{
return fa[a]=find(fa[a]);
}
}
int kruskal(){
int ans=0;int cnt=0;
for(int i=0;i<=m;i++){
fa[i]=i;//初始化,每个顶点都是一个集合
}
//对结构体进行排序·,从小到大,看上面比较运算符
sort(edge, edge+n);
//排序完成,进行边的遍历
for(int i=0;i<n;i++){
//边的两个点是否在同一集合
int fx=find(edge[i].x);
int fy=find(edge[i].y);
//不是就相加 (连接)并查集
//是,再相加(连接)就成回路,所以跳出
if(fx!=fy){
ans+=edge[i].w;
fa[fx]=fy;
cnt++;
}
if(cnt==m-1) break;//直到相加的边等于m-1条边(最小生成树,点全部连起来了)
}
if(cnt<m-1){
ans=-1;
}
return ans;
}
int main(){
while(scanf("%d%d",&n,&m),n){
for(int i=0;i<n;i++){
scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].w);
}
int ans=kruskal();
if(ans == -1) printf("?\n");
else{
printf("%d\n",ans);
}
}
return 0;
}