//土尔逊Torson 编写于2023/07/05
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

const int MAXN = 100;

struct Edge13501 {
	int from;       //边的起点
	int to;         //边的终点
	int length;     //边的长度
	bool operator<(const Edge13501& e) const {
		return length < e.length;
	}
};

Edge13501 edge13501[MAXN * MAXN];   //边集
int father13501[MAXN];              //父亲结点
int height13501[MAXN];              //结点高度

void Initial13501(int n) {     //初始化
	for (int i = 0; i <= n; i++) {
		father13501[i] = i;
		height13501[i] = 0;
	}
}

int Find13501(int x) {         //查找根结点
	if (x != father13501[x]) {
		father13501[x] = Find13501(father13501[x]);
	}
	return father13501[x];
}

void Union13501(int x, int y) { //合并集合
	x = Find13501(x);
	y = Find13501(y);
	if (x != y) {
		if (height13501[x] < height13501[y]) {
			father13501[x] = y;
		}
		else if (height13501[y] < height13501[x]) {
			father13501[y] = x;
		}
		else {
			father13501[y] = x;
			height13501[x]++;
		}
	}
	return;
}

int Kruskal13501(int n, int edgeNumber) {
	Initial13501(n);
	sort(edge13501, edge13501 + edgeNumber);    //按权值排序
	int sum = 0;
	for (int i = 0; i < edgeNumber; ++i) {
		Edge13501 current = edge13501[i];
		if (Find13501(current.from) != Find13501(current.to)) {
			Union13501(current.from, current.to);
			sum += current.length;
		}
	}
	return sum;
}

int main() {
	int n;
	while (scanf("%d", &n) != EOF) {
		if (n == 0) {
			break;
		}
		int edgeNumber = n*(n - 1) / 2;
		for (int i = 0; i < edgeNumber; ++i) {
			int status;
			scanf("%d%d%d%d", &edge13501[i].from, &edge13501[i].to,
				&edge13501[i].length, &status);
			if (status == 1) {
				edge13501[i].length = 0;
			}
		}
		int answer = Kruskal13501(n, edgeNumber);
		printf("%d\n", answer);
	}
	system("pause");
	return EXIT_SUCCESS;
}
// 64 位输出请用 printf("%lld")