链接
解析
不同连通块的最小生成树,用并查集处理一个连通块,进行缩点。之后用最小生成树的prim算法即可。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include<queue>
#include <algorithm>
#include <map>
#include <set>
using namespace std;
const int maxn = 500+10;
const int maxe = 10000+10;
const int inf = 0x3f3f3f3f;
int fa[maxn], num[maxn];
int dis[maxn];
int vis[maxn];
int g[maxn][maxn];
int Find(int x) {
return x==fa[x]?x:fa[x] = Find(fa[x]);
}
void Union(int u, int v) {
int x = Find(u);
int y = Find(v);
if (x == y)
return ;
fa[y] = x;
num[x] += num[y];
num[y] = 0;
}
void init() {
memset(g, inf, sizeof(g));
memset(dis, inf, sizeof(dis));
for (int i=1; i<=maxn ; i++){
fa[i] = i;
num[i] = 1;
g[i][i] = 0;
}
}
struct node {
int u, v, w;
}p[25000+10];
int main() {
int T;
scanf("%d", &T);
while (T--) {
int n, m, k;
init();
scanf("%d%d%d", &n, &m, &k);
for (int i=0; i<m; i++) {
int u, v, w;
scanf("%d%d%d", &p[i].u, &p[i].v, &p[i].w);
}
for (int i=0; i<k; i++) {
int t, u;
scanf("%d%d", &t, &u);
while (t > 1) {
int v;
scanf("%d", &v);
Union(u, v);
t--;
}
}
for (int i=0; i<m; i++) {
int u=Find(p[i].u), v=Find(p[i].v);
int w = p[i].w;
if (u != v) {
g[v][u] = min(g[v][u], w);
g[u][v] = min(g[u][v], w);
}
}
for (int i=0; i<m; i++)
Union(p[i].v, p[i].u);
int s = Find(1);
if (num[s] != n) {
puts("-1");
continue;
}
dis[s] = 0;
memset(vis, 0, sizeof(vis));
int ans=0;
for (int i=1; i<=n; i++)
{
int minn = inf, mark = -1;
for (int j=1; j<=n; j++)
{
if (minn > dis[j] && !vis[j]) {
minn = dis[j];
mark = j;
}
}
if (mark == -1)
break;
ans += minn;
vis[mark] = 1;
for (int j=1; j<=n; j++)
if (g[mark][j] < inf && !vis[j])
dis[j] = min(dis[j], g[mark][j]);
}
printf("%d\n", ans);
}
return 0;
}