#include using namespace std; const int N = 310; int n; // n条顶点 int res; // 最小生成树的权值和 // Kruskal用到的结构体 const int M = 2 * N * N; // 无向图*2,稠密图N*N struct Edge { int a, b, c; const bool operator<(const Edge &t) const { return c < t.c; } } edge[M]; int el; // 边数 // 并查集 int p[N]; int find(int x) { if (p[x] != x) p[x] = find(p[x]); return p[x]; } // Kruskal算法 int kruskal() { // 按边的权重排序 sort(edge, edge + el); // 初始化并查集,注意并查集的初始是从0开始的,因为0号是超级源点 for (int i = 0; i <= n; i++) p[i] = i; // 枚举每条边 for (int i = 0; i < el; i++) { int a = edge[i].a, b = edge[i].b, c = edge[i].c; a = find(a), b = find(b); if (a != b) p[a] = b, res += c; } return res; } int main() { cin >> n; // 建立超级源点(0 <-> 1~n ) int c; for (int i = 1; i <= n; i++) { cin >> c; // 点权转边权 edge[el++] = {0, i, c}; edge[el++] = {i, 0, c}; } // 本题是按矩阵读入的,不是按a,b,c方式读入的 for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) { cin >> c; edge[el++] = {i, j, c}; edge[el++] = {j, i, c}; } // 利用Kruskal计算最小生成树 cout << kruskal() << endl; return 0; }