//刚开始看错题了..看例子以为给你n行,每行意思为a, b之间的距离为dist.(有向).一直找不到自己错在哪 ,,, 然后....看了看别人的发现是无向....n*n位邻接矩阵.............
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int N = 1000+5;
struct Edge{
int u, v, dist;
Edge(){
u = 0;
v = 0;
dist = 0;
};
Edge(int a, int b, int c){
u = a;
v = b;
dist = c;
}
}edge;
bool operator <(Edge a, Edge b)
{
return a.dist > b.dist;
}
priority_queue<Edge> q;
int judge[N][N] = {0};
int pre[N*N/2] = {0};
void init()
{
// memset(judge, 0, sizeof(judge));
for(int i=0; i<N*N/2; i++){
pre[i] = i;
}
}
int Find(int a)
{
return pre[a] == a ? a : Find(pre[a]);
}
void Union(int a, int b)
{
a = Find(a), b = Find(b);
if(a == b){
return;
}
else{
pre[a] = b;
return;
}
}
void Kruskal()
{
int sum = 0;
while(!q.empty()){
edge = q.top();
q.pop();
if(Find(edge.u) != Find(edge.v)){
sum += edge.dist;
Union(edge.u, edge.v);
}
}
cout << sum << endl;
}
int main() {
int n;
while(cin >> n) {
while (!q.empty()) {
q.pop();
}
init();
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
int u, v, l;
// cin >> u >> v >> l;
cin >> l;
// judge[u][v] = 1;
// judge[v][u] = 1;
q.push(Edge(i, j, l));
}
}
int k, a, b;
cin >> k;
while (k--) {
cin >> a >> b;
Union(a, b);
// judge[a][b] = 0;
// judge[b][a] = 0;
}
Kruskal();
}
return 0;
}