#include <cstdio>
#include <vector>
#include <memory.h>
#include <limits>
#include <algorithm>
#include <functional>
#include <queue>
using namespace std;
namespace MCMF {
typedef int cap_t;
typedef double cost_t;
bool isZeroCap(cap_t cap) {
return cap == 0;
}
const cap_t CAP_MAX = numeric_limits<cap_t>::max();
const cost_t COST_MAX = numeric_limits<cost_t>::max();
struct edge_t {
int target;
cap_t cap;
cost_t cost;
int rev;
};
int n;
vector<vector<edge_t>> graph;
vector<cost_t> pi;
vector<cost_t> dist;
vector<cap_t> mincap;
vector<int> from, v;
void init(int _n) {
n = _n;
graph.clear(); graph.resize(n);
pi.clear(); pi.resize(n);
dist.resize(n);
mincap.resize(n);
from.resize(n);
v.resize(n);
}
void addEdge(int a, int b, cap_t cap, cost_t cost) {
edge_t forward = { b, cap, cost, (int)graph[b].size() };
edge_t backward = { a, 0, -cost, (int)graph[a].size() };
graph[a].push_back(forward);
graph[b].push_back(backward);
}
bool dijkstra(int s, int t) {
typedef pair<cost_t, int> pq_t;
priority_queue<pq_t, vector<pq_t>, greater<pq_t>> pq;
fill(dist.begin(), dist.end(), COST_MAX);
memset(&from[0], -1, n*sizeof(from[0]));
memset(&v[0], 0, n*sizeof(v[0]));
dist[s] = 0;
mincap[s] = CAP_MAX;
pq.emplace(make_pair(dist[s], s));
while (!pq.empty()) {
int cur = pq.top().second; pq.pop();
if (v[cur]) continue;
v[cur] = 1;
if (cur == t) continue;
for (int k = 0; k < graph[cur].size(); k++) {
edge_t edge = graph[cur][k];
int next = edge.target;
if (v[next]) continue;
if (isZeroCap(edge.cap)) continue;
cost_t potCost = dist[cur] + edge.cost - pi[next] + pi[cur];
if (dist[next] <= potCost) continue;
dist[next] = potCost;
mincap[next] = min(mincap[cur], edge.cap);
from[next] = edge.rev;
pq.emplace(make_pair(dist[next], next));
}
}
if (dist[t] == COST_MAX) return false;
for (int i = 0; i < n; i++) {
if (dist[i] == COST_MAX) continue;
pi[i] += dist[i];
}
return true;
}
pair<cap_t, cost_t> solve(int source, int sink) {
cap_t total_flow = 0;
cost_t total_cost = 0;
while (dijkstra(source, sink)) {
cap_t f = mincap[sink];
total_flow += f;
for (int p = sink; p != source;) {
auto &backward = graph[p][from[p]];
auto &forward = graph[backward.target][backward.rev];
forward.cap -= f;
backward.cap += f;
total_cost += forward.cost * f;
p = backward.target;
}
}
return make_pair(total_flow, total_cost);
}
}
int x[105], y[105];
int main() {
int runs;
for (scanf("%d", &runs); runs--;) {
int n, m = 0;
scanf("%d", &n);
MCMF::init(2 * n + 2);
int sink = 2 * n + 1;
for (int i = 1; i <= n; i++) {
scanf("%d%d", &x[i], &y[i]);
if (x[i] == 0) { n--; i--; }
}
for (int i = 1; i <= n; i++) {
x[i] = -x[i];
int a = i;
MCMF::addEdge(0, a, 1, 0);
MCMF::addEdge(a, n + a, 1, 2.0*abs(double(x[i])));
for (int j = 1; j <= n; j++) {
if (i != j && x[j] * x[i] > 0) {
int b = n + j;
MCMF::addEdge(a, b, 1, sqrt(double((x[i] - x[j])*(x[i] - x[j]) + (y[i] - y[j])*(y[i] - y[j]))));
}
}
x[i] = -x[i];
MCMF::addEdge(n + i, sink, 1, 0);
}
printf("%.3lf\n", MCMF::solve(0, sink).second / 2.0);
}
}
I2luY2x1ZGUgPGNzdGRpbz4KI2luY2x1ZGUgPHZlY3Rvcj4KI2luY2x1ZGUgPG1lbW9yeS5oPgojaW5jbHVkZSA8bGltaXRzPgojaW5jbHVkZSA8YWxnb3JpdGhtPgojaW5jbHVkZSA8ZnVuY3Rpb25hbD4KI2luY2x1ZGUgPHF1ZXVlPgp1c2luZyBuYW1lc3BhY2Ugc3RkOwpuYW1lc3BhY2UgTUNNRiB7Cgl0eXBlZGVmIGludCBjYXBfdDsKCXR5cGVkZWYgZG91YmxlIGNvc3RfdDsKCWJvb2wgaXNaZXJvQ2FwKGNhcF90IGNhcCkgewoJCXJldHVybiBjYXAgPT0gMDsKCX0KCWNvbnN0IGNhcF90IENBUF9NQVggPSBudW1lcmljX2xpbWl0czxjYXBfdD46Om1heCgpOwoJY29uc3QgY29zdF90IENPU1RfTUFYID0gbnVtZXJpY19saW1pdHM8Y29zdF90Pjo6bWF4KCk7CglzdHJ1Y3QgZWRnZV90IHsKCQlpbnQgdGFyZ2V0OwoJCWNhcF90IGNhcDsKCQljb3N0X3QgY29zdDsKCQlpbnQgcmV2OwoJfTsKCWludCBuOwoJdmVjdG9yPHZlY3RvcjxlZGdlX3Q+PiBncmFwaDsKCXZlY3Rvcjxjb3N0X3Q+IHBpOwoJdmVjdG9yPGNvc3RfdD4gZGlzdDsKCXZlY3RvcjxjYXBfdD4gbWluY2FwOwoJdmVjdG9yPGludD4gZnJvbSwgdjsKCXZvaWQgaW5pdChpbnQgX24pIHsKCQluID0gX247CgkJZ3JhcGguY2xlYXIoKTsgZ3JhcGgucmVzaXplKG4pOwoJCXBpLmNsZWFyKCk7IHBpLnJlc2l6ZShuKTsKCQlkaXN0LnJlc2l6ZShuKTsKCQltaW5jYXAucmVzaXplKG4pOwoJCWZyb20ucmVzaXplKG4pOwoJCXYucmVzaXplKG4pOwoJfQoJdm9pZCBhZGRFZGdlKGludCBhLCBpbnQgYiwgY2FwX3QgY2FwLCBjb3N0X3QgY29zdCkgewoJCWVkZ2VfdCBmb3J3YXJkID0geyBiLCBjYXAsIGNvc3QsIChpbnQpZ3JhcGhbYl0uc2l6ZSgpIH07CgkJZWRnZV90IGJhY2t3YXJkID0geyBhLCAwLCAtY29zdCwgKGludClncmFwaFthXS5zaXplKCkgfTsKCQlncmFwaFthXS5wdXNoX2JhY2soZm9yd2FyZCk7CgkJZ3JhcGhbYl0ucHVzaF9iYWNrKGJhY2t3YXJkKTsKCX0KCWJvb2wgZGlqa3N0cmEoaW50IHMsIGludCB0KSB7CgkJdHlwZWRlZiBwYWlyPGNvc3RfdCwgaW50PiBwcV90OwoJCXByaW9yaXR5X3F1ZXVlPHBxX3QsIHZlY3RvcjxwcV90PiwgZ3JlYXRlcjxwcV90Pj4gcHE7CgkJZmlsbChkaXN0LmJlZ2luKCksIGRpc3QuZW5kKCksIENPU1RfTUFYKTsKCQltZW1zZXQoJmZyb21bMF0sIC0xLCBuKnNpemVvZihmcm9tWzBdKSk7CgkJbWVtc2V0KCZ2WzBdLCAwLCBuKnNpemVvZih2WzBdKSk7CgkJZGlzdFtzXSA9IDA7CgkJbWluY2FwW3NdID0gQ0FQX01BWDsKCQlwcS5lbXBsYWNlKG1ha2VfcGFpcihkaXN0W3NdLCBzKSk7CgkJd2hpbGUgKCFwcS5lbXB0eSgpKSB7CgkJCWludCBjdXIgPSBwcS50b3AoKS5zZWNvbmQ7IHBxLnBvcCgpOwoJCQlpZiAodltjdXJdKSBjb250aW51ZTsKCQkJdltjdXJdID0gMTsKCQkJaWYgKGN1ciA9PSB0KSBjb250aW51ZTsKCQkJZm9yIChpbnQgayA9IDA7IGsgPCBncmFwaFtjdXJdLnNpemUoKTsgaysrKSB7CgkJCQllZGdlX3QgZWRnZSA9IGdyYXBoW2N1cl1ba107CgkJCQlpbnQgbmV4dCA9IGVkZ2UudGFyZ2V0OwoJCQkJaWYgKHZbbmV4dF0pIGNvbnRpbnVlOwoJCQkJaWYgKGlzWmVyb0NhcChlZGdlLmNhcCkpIGNvbnRpbnVlOwoJCQkJY29zdF90IHBvdENvc3QgPSBkaXN0W2N1cl0gKyBlZGdlLmNvc3QgLSBwaVtuZXh0XSArIHBpW2N1cl07CgkJCQlpZiAoZGlzdFtuZXh0XSA8PSBwb3RDb3N0KSBjb250aW51ZTsKCQkJCWRpc3RbbmV4dF0gPSBwb3RDb3N0OwoJCQkJbWluY2FwW25leHRdID0gbWluKG1pbmNhcFtjdXJdLCBlZGdlLmNhcCk7CgkJCQlmcm9tW25leHRdID0gZWRnZS5yZXY7CgkJCQlwcS5lbXBsYWNlKG1ha2VfcGFpcihkaXN0W25leHRdLCBuZXh0KSk7CgkJCX0KCQl9CgkJaWYgKGRpc3RbdF0gPT0gQ09TVF9NQVgpIHJldHVybiBmYWxzZTsKCQlmb3IgKGludCBpID0gMDsgaSA8IG47IGkrKykgewoJCQlpZiAoZGlzdFtpXSA9PSBDT1NUX01BWCkgY29udGludWU7CgkJCXBpW2ldICs9IGRpc3RbaV07CgkJfQoJCXJldHVybiB0cnVlOwoJfQoJcGFpcjxjYXBfdCwgY29zdF90PiBzb2x2ZShpbnQgc291cmNlLCBpbnQgc2luaykgewoJCWNhcF90IHRvdGFsX2Zsb3cgPSAwOwoJCWNvc3RfdCB0b3RhbF9jb3N0ID0gMDsKCQl3aGlsZSAoZGlqa3N0cmEoc291cmNlLCBzaW5rKSkgewoJCQljYXBfdCBmID0gbWluY2FwW3NpbmtdOwoJCQl0b3RhbF9mbG93ICs9IGY7CgkJCWZvciAoaW50IHAgPSBzaW5rOyBwICE9IHNvdXJjZTspIHsKCQkJCWF1dG8gJmJhY2t3YXJkID0gZ3JhcGhbcF1bZnJvbVtwXV07CgkJCQlhdXRvICZmb3J3YXJkID0gZ3JhcGhbYmFja3dhcmQudGFyZ2V0XVtiYWNrd2FyZC5yZXZdOwoJCQkJZm9yd2FyZC5jYXAgLT0gZjsKCQkJCWJhY2t3YXJkLmNhcCArPSBmOwoJCQkJdG90YWxfY29zdCArPSBmb3J3YXJkLmNvc3QgKiBmOwoJCQkJcCA9IGJhY2t3YXJkLnRhcmdldDsKCQkJfQoJCX0KCQlyZXR1cm4gbWFrZV9wYWlyKHRvdGFsX2Zsb3csIHRvdGFsX2Nvc3QpOwoJfQp9CmludCB4WzEwNV0sIHlbMTA1XTsKaW50IG1haW4oKSB7CglpbnQgcnVuczsKCWZvciAoc2NhbmYoIiVkIiwgJnJ1bnMpOyBydW5zLS07KSB7CgkJaW50IG4sIG0gPSAwOwoJCXNjYW5mKCIlZCIsICZuKTsKCQlNQ01GOjppbml0KDIgKiBuICsgMik7CgkJaW50IHNpbmsgPSAyICogbiArIDE7CgkJZm9yIChpbnQgaSA9IDE7IGkgPD0gbjsgaSsrKSB7CgkJCXNjYW5mKCIlZCVkIiwgJnhbaV0sICZ5W2ldKTsKCQkJaWYgKHhbaV0gPT0gMCkgeyBuLS07IGktLTsgfQoJCX0KCQlmb3IgKGludCBpID0gMTsgaSA8PSBuOyBpKyspIHsKCQkJeFtpXSA9IC14W2ldOwoJCQlpbnQgYSA9IGk7CgkJCU1DTUY6OmFkZEVkZ2UoMCwgYSwgMSwgMCk7CgkJCU1DTUY6OmFkZEVkZ2UoYSwgbiArIGEsIDEsIDIuMCphYnMoZG91YmxlKHhbaV0pKSk7CgkJCWZvciAoaW50IGogPSAxOyBqIDw9IG47IGorKykgewoJCQkJaWYgKGkgIT0gaiAmJiB4W2pdICogeFtpXSA+IDApIHsKCQkJCQlpbnQgYiA9IG4gKyBqOwoJCQkJCU1DTUY6OmFkZEVkZ2UoYSwgYiwgMSwgc3FydChkb3VibGUoKHhbaV0gLSB4W2pdKSooeFtpXSAtIHhbal0pICsgKHlbaV0gLSB5W2pdKSooeVtpXSAtIHlbal0pKSkpOwoJCQkJfQoJCQl9CgkJCXhbaV0gPSAteFtpXTsKCQkJTUNNRjo6YWRkRWRnZShuICsgaSwgc2luaywgMSwgMCk7CgkJfQoJCXByaW50ZigiJS4zbGZcbiIsIE1DTUY6OnNvbHZlKDAsIHNpbmspLnNlY29uZCAvIDIuMCk7Cgl9Cn0=