/*初学时有点懵懂和dijkstra总是搞不懂,简单区别为prim算法为一个连通图总和的最短长度,dijkstra算法为给定起始点一个或多个终点的最短长度*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int INF = 0x3f3f3f3f;
int vis[2000], dis[2000], graph[2000][2000]; //vis标记 dis其实抽象为边的集合 graph[a][b]从a到b长度
int n, m;
int prime()
{
int index;
int sum = 0;
vis[1] = 1;
for(int i = 1; i <= n; i++){ //prim的思想是通过不断将最短的边纳入一个集合完成,对于变而言是基于点的基础的,所以随便默认一个初始点通过对电的操作完成对边的操作
dis[i] = graph[1][i]; //这里是1号点与所有点的距离
}
for(int i = 1; i < n; i++){ //因为n个点之间只有n-1条边所以 循环纳入集合的次数为n-1 而不是<=n
int min = INF;
for(int j = 1; j <= n; j++){ //这里是以一个点为根节点或者以一个前驱集合为基础找到一条与之最短的边标记然后对所带点操作完成功能
if(!vis[j] && dis[j] < min){
min = dis[j];
index = j;
}
}
vis[index] = 1;
sum += min;
for(int j = 1; j <= n; j ++){
if(!vis[j] && dis[j] > graph[index][j]){ //这里的dis是上一个集合或者点与除了刚纳入集合边所带的点的距离,这里很厉害 dis[j] > graph[index][j] 之前所纳入集合的边所带的点与剩下的点之间的距离大还是之前集合离剩下的点距离大(很强可以画图便于理解)
dis[j] = graph[index][j];
}
}
}
return sum;
}
void init()
{
memset(vis, 0, sizeof(vis));
memset(dis, 0, sizeof(dis));
for(int i = 0; i <= n; i++){
for(int j = 0; j <= n; j++){
graph[i][j] = INF;
graph[i][i] = 0;
}
}
return ;
}
int main()
{
while(scanf("%d", &n) != EOF && n){
scanf("%d", &m);
init();
int a, b, len;
int len_min = INF;
for(int j = 1; j <= m; j++){
scanf("%d%d%d", &a, &b, &len);
if(graph[a][b] > len){
graph[a][b] = len;
graph[b][a] = len; //这里注意双向边那么有可能 a->b的len不等于b->a的len
}
}
cout << prime() << endl;
}
return 0;
}