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