#include<bits/stdc++.h>
using namespace std;
int N, a, b, Cost, state, edge_num;
struct edge {
int one_pos;
int another_pos;
int cost;
};
edge e;
int root[101];
vector<edge> edges;//边的集合
//克鲁斯卡尔算法
int Kruskal() {
int min_cost = 0;//最少花费
//初始化:每一个顶点(村庄)都是一棵树,一共n棵树
for (int i = 1; i <= N; i++) {
root[i] = i; //自己的根是自己
}
for (int i = 0; i < edge_num; i++)
{
//如果不是一个集合,那么将两个集合(树)合并,并且将这条边的花费加入最小生成树的花费
if (root[edges[i].one_pos] != root[edges[i].another_pos])
{
if (root[edges[i].one_pos] < root[edges[i].another_pos])
{
int small = root[edges[i].one_pos];
int big = root[edges[i].another_pos];
for (int k = 1; k <= N; k++)
{
if (root[k] ==big)
{
root[k] = small;
}
}
}
else
{
int big = root[edges[i].one_pos];
int small = root[edges[i].another_pos];
for (int k = 1; k <= N; k++)
{
if (root[k] == big)
{
root[k] =small;
}
}
}
min_cost += edges[i].cost;//将这条边的花费加入最小生成树的花费
}
//看看是不是所有村庄都汇集成了一棵大树
bool flag = 1;//假设汇集成了
for (int c = 1; c <= N; c++)
{
if (root[c] != 1)
{
flag = 0;
break;
}
}
if (flag == 1) break;
}
return min_cost;
}
int main() {
while (cin >> N && N) {
edges.clear();//清理上一轮的
edge_num = (N - 1) * N / 2; //无向图的边的数目
for (int i = 0; i < edge_num; i++) {
cin >> a >> b >> Cost >> state;
e.one_pos = a;
e.another_pos = b;
e.cost = Cost;
if (state == 1) e.cost = 0;
edges.push_back(e);//装入边集合
}
//将边按照cost排序
sort(edges.begin(), edges.end(), [](const edge& a, const edge& b) {
return a.cost < b.cost;
});
//克鲁斯卡尔算法
cout << Kruskal() << endl;
}
}
// 64 位输出请用 printf("%lld")