强连通分量
POJ 2186 popular cow 从一个没有入度的连通分量遍历图
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<string>
#include<queue>
#include<ctype.h>
#include<map>
#include<math.h>
using namespace std;
const int INF = 1 << 30;
int n, m;
vector<vector<int> > g(10010);
vector<vector<int> > rg(10010);
vector<int> order;
int cnt;
int visited[10010];
int color[10010];
void dfs(int v)
{
visited[v] = 1;
for (int i = 0; i < g[v].size(); i++)
{
if (!visited[g[v][i]])
dfs(g[v][i]);
}
order.push_back(v);
}
void rdfs(int v, int k)
{
color[v] = k;
visited[v] = 1;
for (int i = 0; i < rg[v].size(); i++)
{
if (!visited[rg[v][i]])
rdfs(rg[v][i], k);
}
}
int main()
{
scanf("%d%d", &n, &m);
int a, b;
for (int i = 0; i < m; i++)
{
scanf("%d%d", &a, &b);
g[a].push_back(b);
rg[b].push_back(a);
}
memset(visited, 0, sizeof(visited));
for (int i = 1; i <= n; i++)
{
if (!visited[i])
dfs(i);
}
cnt = 0;
memset(visited, 0, sizeof(visited));
for (int i = order.size() - 1; i >= 0; i--)
{
if (!visited[order[i]])
rdfs(order[i], cnt++);
}
int ans = 0;
int u;
for (int i = 1; i <= n; i++)
if (color[i] == cnt - 1)
{
ans++;
u = i;
}
memset(visited, 0, sizeof(visited));
rdfs(u, 0);
for (int i = 1; i <= n; i++)
if (visited[i] == 0)
{
ans = 0;
break;
}
printf("%d\n", ans);
}
POJ 2186 popular cow 看每个连通分量的入度
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<string>
#include<queue>
#include<ctype.h>
#include<map>
#include<math.h>
using namespace std;
const int INF = 1 << 30;
int n, m;
vector<vector<int> > g(10010);
vector<vector<int> > rg(10010);
vector<int> order;
int cnt;
int visited[10010];
vector<vector<int> > colorSet;
int colorHash[10010];
void dfs(int v)
{
visited[v] = 1;
for (int i = 0; i < g[v].size(); i++)
{
if (!visited[g[v][i]])
dfs(g[v][i]);
}
order.push_back(v);
}
void rdfs(int v, int k)
{
colorSet[k].push_back(v);
colorHash[v] = k;
visited[v] = 1;
for (int i = 0; i < rg[v].size(); i++)
{
if (!visited[rg[v][i]])
rdfs(rg[v][i], k);
}
}
int main()
{
scanf("%d%d", &n, &m);
int a, b;
for (int i = 0; i < m; i++)
{
scanf("%d%d", &a, &b);
g[a].push_back(b);
rg[b].push_back(a);
}
memset(visited, 0, sizeof(visited));
for (int i = 1; i <= n; i++)
{
if (!visited[i])
dfs(i);
}
cnt = 0;
memset(visited, 0, sizeof(visited));
for (int i = order.size() - 1; i >= 0; i--)
{
if (!visited[order[i]])
{
colorSet.push_back(vector<int>());
rdfs(order[i], cnt++);
}
}
int ans = 0;
int endPoint = 0;
for (int i = 0; i < colorSet.size(); i++)//每个连通分支
{
int tag = 0;
for (int j = 0; j < colorSet[i].size(); j++)//每个连通分支的每个点
{
int u = colorSet[i][j];
for (int k = 0; k < g[u].size(); k++)//一个连通分支的一个点的边
{
if (colorHash[g[u][k]] != i)
{
tag = 1;
break;
}
}
if (tag == 1)
break;
}
if (tag == 0)
{
endPoint++;
ans = colorSet[i].size();
}
}
if (endPoint != 1)
ans = 0;
printf("%d\n", ans);
}
POJ 1236 网络传输
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<string>
#include<queue>
#include<ctype.h>
#include<map>
#include<math.h>
using namespace std;
const int INF = 1 << 30;
int n;
vector<vector<int> > g;
vector<vector<int> > rg;
vector<int> dfsOrder;
int ncolor;
int visited[110];
vector<vector<int> > colorSet;
int colorHash[110];
void dfs(int v)
{
visited[v] = 1;
for (int i = 0; i < g[v].size(); i++)
{
if (!visited[g[v][i]])
dfs(g[v][i]);
}
dfsOrder.push_back(v);
}
void rdfs(int v, int k)
{
colorSet[k].push_back(v);
colorHash[v] = k;
visited[v] = 1;
for (int i = 0; i < rg[v].size(); i++)
{
if (!visited[rg[v][i]])
rdfs(rg[v][i], k);
}
}
int main()
{
scanf("%d", &n);
g.resize(n + 1);
rg.resize(n + 1);
int u;
for (int i = 1; i <= n; i++)
{
while (scanf("%d", &u) == 1 && u != 0)
{
g[i].push_back(u);
rg[u].push_back(i);
}
}
memset(visited, 0, sizeof(visited));
for (int i = 1; i <= n; i++)
{
if (!visited[i])
dfs(i);
}
ncolor = 0;
memset(visited, 0, sizeof(visited));
for (int i = dfsOrder.size() - 1; i >= 0; i--)
{
if (!visited[dfsOrder[i]])
{
colorSet.push_back(vector<int>());
rdfs(dfsOrder[i], ncolor++);
}
}
int ans1 = 0;
int startPoint = 0;
for (int i = 0; i < colorSet.size(); i++)
{
int tag = 1;
for (int j = 0; j < colorSet[i].size(); j++)
{
int u = colorSet[i][j];
for (int k = 0; k < rg[u].size(); k++)//一个连通分支的一个点的边
{
if (colorHash[rg[u][k]] != i)
{
tag = 0;
break;
}
}
}
if (tag)
ans1++;
}
int ans2 = 0;
int endPoint = 0;
for (int i = 0; i < colorSet.size(); i++)//每个连通分支
{
int tag = 0;
for (int j = 0; j < colorSet[i].size(); j++)//每个连通分支的每个点
{
int u = colorSet[i][j];
for (int k = 0; k < g[u].size(); k++)//一个连通分支的一个点的边
{
if (colorHash[g[u][k]] != i)
{
tag = 1;
break;
}
}
if (tag == 1)
break;
}
if (tag == 0)
{
endPoint++;
}
}
ans2 = max(ans1, endPoint);
if (ncolor == 1)
{
printf("%d\n", ans1);
printf("%d\n", 0);
}
else
{
printf("%d\n", ans1);
printf("%d\n", ans2);
}
}
tarjan 求割点和桥
#include <iostream>
#include <vector>
#include<algorithm>
using namespace std;
#define MAXN 200
typedef vector<int> Edge;
vector<Edge> G(MAXN);
bool Visited[MAXN];
int dfn[MAXN];
int low[MAXN];
int Father[MAXN]; //DFS树中每个点的父节点
bool isCutVex[MAXN]; //每个点是不是割点
int nTime; //Dfs时间戳
int n, m; //n是点数, m是边数
void Tarjan(int u, int father) //father 是u的父节点
{
Father[u] = father;
int i, j, k;
low[u] = dfn[u] = nTime++;
for (i = 0; i < G[u].size(); i++)
{
int v = G[u][i];
if (!dfn[v])
{
Tarjan(v, u);
low[u] = min(low[u], low[v]);
}
else if (father != v) //连到父节点的回边不考虑,否则求不出桥
low[u] = min(low[u], dfn[v]);
}
}void Count()
{ //计算割点和桥
int nRootSons = 0;
int i;
Tarjan(1, 0);
for (i = 2; i <= n; i++)
{
int v = Father[i];
if (v == 1)
nRootSons++; //DFS树中根节点有几个子树
else
{
if (dfn[v] <= low[i])
isCutVex[v] = true;
}
}
if (nRootSons > 1)
isCutVex[1] = true;
for (i = 1; i <= n; i++)
if (isCutVex[i])
cout << i << endl;
for (i = 1; i <= n; i++)
{
int v = Father[i];
if (v > 0 && dfn[v] < low[i])
cout << v << "," << i << endl;
}
}int main()
{
int u, v;
int i;
nTime = 1;
cin >> n >> m; //n是点数, m是边数
for (i = 1; i <= m; i++)
{
cin >> u >> v; //点编号从1开始
G[v].push_back(u);
G[u].push_back(v);
}
memset(dfn, 0, sizeof(dfn));
memset(Father, 0, sizeof(Father));
memset(isCutVex, 0, sizeof(isCutVex));
Count();
return 0;
}
tarjan 求双连通分量
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
#define MAXN 200
vector<vector<int> > G(MAXN);
int dfn[MAXN];
int low[MAXN];
int nTime;
int n, m; //n是点数, m是边数
struct Edge
{
int u;
int v;
Edge()
{
}
Edge(int u_, int v_) :u(u_), v(v_)
{
}
};
deque<Edge> Edges;
int nBlockNo = 0;
void Tarjan(int u, int father)
{
int i, j, k;
low[u] = dfn[u] = nTime++;
for (i = 0; i < G[u].size(); i++)
{
int v = G[u][i];
if (!dfn[v])
{ //v没有访问过
//树边要入栈
Edges.push_back(Edge(u, v));
Tarjan(v, u);
low[u] = min(low[u], low[v]);
Edge tmp;
if (dfn[u] <= low[v])
{
//从一条边往下走,走完后发现自己是割点,则栈中的边一定全是和自己在一个双连通分量里面
//根节点总是和其下的某些点在同一个双连通分量里面
cout << "Block No: " << ++nBlockNo << endl;
do
{
tmp = Edges.back();
Edges.pop_back();
cout << tmp.u << "," << tmp.v << endl;
} while (!(tmp.u == u &&tmp.v == v));
}
} // 对应if( ! dfn[v]) {
else
{
if (v != father)
{//u连到父节点的回边不考虑
low[u] = min(low[u], dfn[v]);
if (dfn[u] > dfn[v])
//连接到祖先的回边要入栈,但是连接到儿子的边,此处肯定已经入过栈了,不能再入栈
Edges.push_back(Edge(u, v));
}
}
} //对应 for( i = 0;i < G[u].size() ;i ++ ) {
}
int main()
{
int u, v;
int i;
nTime = 1;
cin >> n >> m; //n是点数, m是边数
nBlockNo = 0;
for (i = 1; i <= m; i++)
{
cin >> u >> v; //点编号从1开始
G[v].push_back(u);
G[u].push_back(v);
}
memset(dfn, 0, sizeof(dfn));
Tarjan(1, 0);
return 0;
}
POJ 3352 道路重建
#include <vector>
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<stack>
using namespace std;
#define MAXN 1010
typedef vector<int> Edge;
vector<Edge> g(MAXN);
int dfn[MAXN];
int low[MAXN];
int nTime; //Dfs时间戳
int n, m; //n是点数, m是边数
int degree[MAXN];
stack<int> s;
int colorHash[MAXN];
int ncolor;
void Tarjan(int u, int father) //father 是u的父节点
{
int i;
s.push(u);
low[u] = dfn[u] = nTime++;
for (i = 0; i < g[u].size(); i++)
{
int v = g[u][i];
if (!dfn[v])
{
Tarjan(v, u);
low[u] = min(low[u], low[v]);
}
else if (father != v) //连到父节点的回边不考虑,否则求不出桥
low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u])
{
int temp;
do
{
temp = s.top();
s.pop();
colorHash[temp] = ncolor;
} while (!s.empty() && temp != u);
ncolor++;
}
}
void Count()
{
Tarjan(1, 0);
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < g[i].size(); j++)
{
int v = g[i][j];
if (colorHash[i] != colorHash[v])
{
degree[colorHash[v]]++;
degree[colorHash[i]]++;
}
}
}
int leaf = 0;
for (int i = 0; i < ncolor; i++)
{
if (degree[i] == 2)
leaf++;
}
printf("%d\n", (leaf + 1) / 2);
}
int main()
{
while (scanf("%d%d", &n, &m) == 2) //n是点数, m是边数
{
int u, v;
nTime = 1;
memset(dfn, 0, sizeof(dfn));
memset(degree, 0, sizeof(degree));
for (int i = 0; i < MAXN; i++)
g[i].clear();
for (int i = 1; i <= m; i++)
{
scanf("%d%d", &u, &v); //点编号从1开始
g[v].push_back(u);
g[u].push_back(v);
}
Count();
}
}
最短路
POJ 3159 糖果Dijkstra
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<string>
#include<queue>
#include<ctype.h>
#include<map>
#include<math.h>
using namespace std;
const int INF = 1 << 30;
struct Edge
{
int v;
int w;
Edge()
{
}
Edge(int _v, int _w) :v(_v), w(_w)
{
}
};
bool operator<(const Edge & lhs, const Edge & rhs)
{
return lhs.w>rhs.w;
}
priority_queue<Edge> pq;
bool isUsed[30010];
vector<vector<Edge> > g;
int n, m;
int main()
{
scanf("%d%d", &n, &m);
g.clear();
g.resize(n + 1);
memset(isUsed, 0, sizeof(isUsed));
int a, b, c;
for (int i = 1; i <= m; i++)
{
scanf("%d%d%d", &a, &b, &c);
g[a].push_back(Edge(b, c));
}
Edge e(1, 0);
pq.push(e);
while (!pq.empty())
{
e = pq.top();
pq.pop();
if (isUsed[e.v])
continue;
isUsed[e.v] = true;
if (e.v == n)
{
printf("%d\n", e.w);
return 0;
}
for (int i = 0; i<g[e.v].size(); i++)
{
if (isUsed[g[e.v][i].v])
continue;
pq.push(Edge(g[e.v][i].v, e.w + g[e.v][i].w));
}
}
}
POJ 3259 Bellman-Ford 判断有无负权值回路
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<string>
#include<queue>
#include<ctype.h>
#include<map>
#include<math.h>
using namespace std;
const int INF = 1 << 30;
struct Edge
{
int u;
int v;
int w;
Edge()
{
}
Edge(int _u, int _v, int _w) :u(_u), v(_v), w(_w)
{
}
};
vector<Edge> g;
int n, m, w;
bool Bellman_Ford(int v)
{
vector<int> dist(n + 1);
for (int i = 1; i <= n; i++)
dist[i] = INF;
dist[v] = 0;
for (int i = 1; i<n; i++)
{
int flag=0;
for (int j = 0; j<g.size(); j++)
{
int s = g[j].u;
int e = g[j].v;
if (dist[s] + g[j].w<dist[e])
{
dist[e] = dist[s] + g[j].w;
flag=1;
}
}
if(flag==0)
return false;
}
for (int i = 0; i<g.size(); i++)
{
int s = g[i].u;
int e = g[i].v;
if (dist[s] + g[i].w<dist[e])
return true;
}
return false;
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
g.clear();
scanf("%d%d%d", &n, &m, &w);
int s, e, t;
for (int i = 1; i <= m; i++)
{
scanf("%d%d%d", &s, &e, &t);
g.push_back(Edge(s, e, t));
g.push_back(Edge(e, s, t));
}
for (int i = 1; i <= w; i++)
{
scanf("%d%d%d", &s, &e, &t);
g.push_back(Edge(s, e, -t));
}
if (Bellman_Ford(1))
printf("YES\n");
else
printf("NO\n");
}
}
POJ 3259 SPFA 判断有无负权值回路
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<string>
#include<queue>
#include<ctype.h>
#include<map>
#include<math.h>
using namespace std;
const int INF = 1 << 30;
struct Edge
{
int v;
int w;
Edge()
{
}
Edge(int _v, int _w) : v(_v), w(_w)
{
}
};
vector<vector<Edge> >g;
int n, m, w;
bool spfa(int v)
{
vector<int> vDist(n + 1);
vector<int> updateTimes(n + 1);
vector<bool> inqueue(n + 1);
for (int i = 1; i <= n; i++)
{
vDist[i] = INF;
updateTimes[i] = 0;
inqueue[i] = false;
}
vDist[v] = 0;
queue<int> q;
q.push(v);
inqueue[v] = true;
while (!q.empty())
{
int u = q.front();
q.pop();
inqueue[u] = false;
for (int i = 0; i < g[u].size(); i++)
{
int v = g[u][i].v;
if (vDist[v] > vDist[u] + g[u][i].w)
{
vDist[v] = vDist[u] + g[u][i].w;
if (inqueue[v] == false)
{
q.push(v);
}
++updateTimes[v];
if (updateTimes[v] >= n)
return true;
}
}
}
return false;
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
scanf("%d%d%d", &n, &m, &w);
g.clear();
g.resize(n + 1);
int u, v, t;
for (int i = 1; i <= m; i++)
{
scanf("%d%d%d", &u, &v, &t);
g[u].push_back(Edge(v, t));
g[v].push_back(Edge(u, t));
}
for (int i = 1; i <= w; i++)
{
scanf("%d%d%d", &u, &v, &t);
g[u].push_back(Edge(v, -t));
}
if (spfa(1))
printf("YES\n");
else
printf("NO\n");
}
}
POJ 3660 确定名次 弗洛伊德算法
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<string>
#include<queue>
#include<ctype.h>
#include<map>
#include<math.h>
using namespace std;
const int INF = 1 << 30;
int n, m;
int g[110][110];
int dist[110][110];
int main()
{
scanf("%d%d", &n, &m);
int a, b;
memset(g, 0, sizeof(g));
for (int i = 0; i < 110; i++)
for (int j = 0; j < 110; j++)
{
dist[i][j] = INF;
}
for (int i = 0; i < m; i++)
{
scanf("%d%d", &a, &b);
dist[a][b] = g[a][b] = 1;
}
for (int k = 1; k <= n; k++)
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (dist[i][k] != INF && dist[k][j] != INF && dist[i][k] + dist[k][j] < dist[i][j])
{
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
}
int ans = 0;
for (int i = 1; i <= n; i++)
{
int cnt = 0;
for (int j = 1; j <= n; j++)
{
if (dist[i][j] != INF || dist[j][i] != INF)
cnt++;
}
if (cnt == n - 1)
ans++;
}
printf("%d\n", ans);
}
最小生成树
POJ 1258 kruskal模板
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<string>
#include<queue>
#include<ctype.h>
#include<map>
#include<math.h>
using namespace std;
struct Edge
{
int u, v, w;
Edge(int _u, int _v, int _w) :u(_u), v(_v), w(_w)
{
}
Edge()
{
}
bool operator<(const Edge & rhs)const
{
return w < rhs.w;
}
};
vector<Edge> g;
vector<int> parent;
int n;
int getParent(int a)
{
if (parent[a] == a)
return a;
return parent[a] = getParent(parent[a]);
}
void merge(int a, int b)
{
int p1 = getParent(a);
int p2 = getParent(b);
if (p1 == p2)
return;
parent[p2] = p1;
}
int main()
{
while (scanf("%d", &n) == 1)
{
parent.clear();
g.clear();
for (int i = 0; i < n; i++)
parent.push_back(i);
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
{
int w;
scanf("%d", &w);
g.push_back(Edge(i, j, w));
}
sort(g.begin(), g.end());
int done = 0;
int totalLen = 0;
for (int i = 0; i < g.size(); i++)
{
if (getParent(g[i].u) != getParent(g[i].v))
{
merge(g[i].u, g[i].v);
++done;
totalLen += g[i].w;
}
if (done == n - 1)
break;
}
printf("%d\n", totalLen);
}
}
POJ 1258 primHeap模板
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<string>
#include<queue>
#include<ctype.h>
#include<map>
#include<math.h>
using namespace std;
const int INF = 1 << 30;
struct Edge
{
int v, w;
Edge(int _v = 0, int _w = INF) :v(_v), w(_w)
{
}
bool operator<(const Edge & e)const
{
return w > e.w;
}
};
vector<vector<Edge> > g(110);
int n;
int heapPrim()
{
Edge xDist;
priority_queue<Edge> pq;
vector<int> vDist(n);
vector<int> vUsed(n);
int nDoneNum = 0;
for (int i = 0; i < n; i++)
{
vDist[i] = INF;
vUsed[i] = 0;
}
int nTotalW = 0;
pq.push(Edge(0, 0));
while (nDoneNum < n && !pq.empty())
{
do
{
xDist = pq.top();
pq.pop();
} while (vUsed[xDist.v] == 1 && !pq.empty());
if (vUsed[xDist.v] == 0)
{
nTotalW += xDist.w;
vUsed[xDist.v] = 1;
nDoneNum++;
for (int i = 0; i < g[xDist.v].size(); i++)
{
int k = g[xDist.v][i].v;
if (vUsed[k] == 0)
{
int w = g[xDist.v][i].w;
if (vDist[i] > w)
{
vDist[i] = w;
pq.push(Edge(k, w));
}
}
}
}
}
if (nDoneNum < n)
return -1;
return nTotalW;
}
int main()
{
while (scanf("%d", &n) == 1)
{
for (int i = 0; i < n; i++)
g[i].clear();
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
{
int w;
scanf("%d", &w);
g[i].push_back(Edge(j, w));
}
printf("%d\n", heapPrim());
}
}
POJ 2349 村庄连接 double用%f
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<string>
#include<queue>
#include<ctype.h>
#include<map>
#include<math.h>
using namespace std;
struct Edge
{
int u, v;
double w;
bool operator<(const Edge & rhs)const
{
return w < rhs.w;
}
};
Edge g[1000010];
int parent[550];
int n, t, s;
int getParent(int a)
{
if (parent[a] == a)
return a;
return parent[a] = getParent(parent[a]);
}
void merge(int a, int b)
{
int p1 = getParent(a);
int p2 = getParent(b);
if (p1 == p2)
return;
parent[p1] = p2;
}
int x[550], y[550];
double ans[550];
int main()
{
scanf("%d", &t);
while (t--)
{
scanf("%d%d", &s, &n);
for (int i = 0; i < n; i++)
parent[i] = i;
int ncount = 0;
for (int i = 0; i < n; i++)
{
scanf("%d%d", &x[i], &y[i]);
for (int j = 0; j < i; j++)
{
double dist = sqrt((x[i] - x[j])*(x[i] - x[j]) + (y[i] - y[j])*(y[i] - y[j]));
g[ncount].u = i;
g[ncount].v = j;
g[ncount].w = dist;
ncount++;
}
}
sort(g, g + ncount);
int done = 0;
for (int i = 0; i < ncount; i++)
{
if (getParent(g[i].u) != getParent(g[i].v))
{
merge(g[i].u, g[i].v);
ans[done++] = g[i].w;
if (done == n - s)
{
printf("%.2f\n", g[i].w);
break;
}
}
}
}
}
UVA 1494 秦始皇修路
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<string>
#include<queue>
#include<ctype.h>
#include<map>
#include<math.h>
using namespace std;
const int INF = 1 << 30;
struct Edge
{
int v;
double w;
Edge()
{
}
Edge(int _v, double _w) :v(_v), w(_w)
{
}
bool operator<(const Edge & e)const
{
return w > e.w;
}
};
double g[1010][1010];
int n;
double max_val[1010][1010];
int x[1010];
int y[1010];
int p[1010];
double heapPrim()
{
Edge xDist;
priority_queue<Edge> pq;
vector<double> vDist(n);
vector<int> vUsed(n);
vector<int> vLast(n);
int nDoneNum = 0;
for (int i = 0; i < n; i++)
{
vDist[i] = INF;
vUsed[i] = 0;
vLast[i] = 0;
}
double nTotalW = 0;
pq.push(Edge(0, 0));
while (nDoneNum < n && !pq.empty())
{
do
{
xDist = pq.top();
pq.pop();
} while (vUsed[xDist.v] == 1 && !pq.empty());
if (vUsed[xDist.v] == 0)
{
nTotalW += xDist.w;
vUsed[xDist.v] = 1;
nDoneNum++;
max_val[vLast[xDist.v]][xDist.v] = max_val[xDist.v][vLast[xDist.v]] = xDist.w;
for (int i = 0; i < n; i++)
{
if (vUsed[i] == 1 && i != vLast[xDist.v])
{
max_val[i][xDist.v] = max_val[xDist.v][i] = max(max_val[i][vLast[xDist.v]], max_val[vLast[xDist.v]][xDist.v]);
}
}
for (int i = 0; i < n; i++)
{
if (vUsed[i] == 0)
{
double w = g[xDist.v][i];
if (vDist[i] > w)
{
vDist[i] = w;
vLast[i] = xDist.v;
pq.push(Edge(i, w));
}
}
}
}
}
if (nDoneNum < n)
exit(1);
return nTotalW;
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
scanf("%d", &n);
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
max_val[i][j] = -INF;
for (int i = 0; i < n; i++)
{
scanf("%d%d%d", &x[i], &y[i], &p[i]);
for (int j = 0; j < i; j++)
{
double dist = sqrt((x[i] - x[j])*(x[i] - x[j]) + (y[i] - y[j])*(y[i] - y[j]));
g[i][j] = g[j][i] = dist;
}
}
double totalW = heapPrim();
double ans = -INF;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < i; j++)
{
ans = max(ans, (p[i] + p[j]) / (totalW - max_val[i][j]));
}
}
printf("%.2f\n", ans);
}
}