/*
* 蛮水的一道MST Kruskal, 链表实现
*
* 预处理:
* 根据已有的边,把相应的集合合并。。再Kruskal
*
* 另一种方法:
* 把已有边的权值改为0
*
*/
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxN = 100 + 5;
int n, totEdgeNum, G[maxN][maxN], q, ans;
struct SEdge{
int w, u, v;
};
SEdge edge[maxN * maxN];
struct SNode{
int num, len;
SNode *rep, *next;
};
SNode *head[maxN], *tail[maxN], *node[maxN];
SNode* makeSet(SNode *x){
head[x->num] = x; tail[x->num] = x;
x->rep = x; x->next = NULL;
x->len = 1;
return x;
}
SNode* findSet(SNode *x){
return x->rep;
}
SNode* unionSet(SNode *lhs, SNode *rhs){
SNode *first, *second, *cur;
if(lhs->rep->len >= rhs->rep->len){
first = lhs->rep; second = rhs->rep;
}
else{
first = rhs->rep; second = lhs->rep;
}
cur = second;
while(cur != NULL){
cur->rep = first;
cur = cur->next;
}
tail[first->num]->next = head[second->num];
tail[first->num] = tail[second->num];
head[second->num] = head[first->num];
return first;
}
int cmp(const void *lhs, const void *rhs){
SEdge *a = (SEdge *)lhs; SEdge *b = (SEdge *)rhs;
return a->w - b->w;
}
void mstKruskal(){
qsort(edge, totEdgeNum, sizeof(SEdge), cmp);
/*
for(int i=0; i<totEdgeNum; i++){
printf("%d ", edge[i].w);
}
printf("\n");
*/
for(int i=0; i<totEdgeNum; i++){
if(findSet(node[edge[i].u]) != findSet(node[edge[i].v])){
ans += edge[i].w;
unionSet(node[edge[i].u], node[edge[i].v]);
}
}
}
int main(){
//图输入
scanf("%d", &n);
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
scanf("%d", &G[i][j]);
}
}
totEdgeNum = 0;
for(int i=0; i<n; i++){
for(int j=0; j<=i; j++){
edge[totEdgeNum].u = i;
edge[totEdgeNum].v = j;
edge[totEdgeNum].w = G[i][j];
totEdgeNum++;
}
}
//初始化
for(int i=0; i<n; i++){
node[i] = new SNode;
node[i]->num = i;
makeSet(node[i]);
}
scanf("%d", &q);
int u, v;
for(int i=0; i<q; i++){
scanf("%d%d", &u, &v);
if(node[u-1]->rep != node[v-1]->rep)
unionSet(node[u-1], node[v-1]);
}
//kruskal
mstKruskal();
printf("%d\n", ans);
return 0;
}