本题就是给定一个图,有些点已经有边了, 在有边的情况下求出最小生成树。
如果用kruskal算法,已经存在的边实际上是在为我们初始化并查集,我们要把这些边的左右端点的集合并起来就行了。
在完成了并查集的初始化,就可以套模板求最小生成树了。代码如下:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <queue>
#include <map>
using namespace std;
typedef long long int LL;
const int maxn = 1005;
const int INF = 0x3f3f3f3f;
int p[maxn], n, m;
struct Node{
int x, y;
};
Node a[maxn];
struct edge{
int u, v;
double dis;
edge(int a, int b, double c){u = a; v = b; dis = c;}
edge();
bool operator < (const edge &b) const{
return dis < b.dis;
}
};
vector<edge> edges;
int find_root(int x){//并查集找根节点
if (p[x] == x) return x;
return p[x] = find_root(p[x]);
}
double kruskal(){//模板
sort(edges.begin(), edges.end());
double res = 0;
for (int i = 0; i < edges.size(); i++){
edge e = edges[i];
int x = e.u, y = e.v;
double cost = e.dis;
int rx = find_root(x);
int ry = find_root(y);
if (rx != ry){
p[rx] = ry;
res += cost;
}
}
return res;
}
int main()
{
//freopen("1.txt", "r", stdin);
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++){
scanf("%d%d", &a[i].x, &a[i].y);
for (int j = 1; j <= i - 1; j++){//因为没写double的类型转换wa了无数次
double cost = sqrt((double)(a[i].x - a[j].x) * (a[i].x - a[j].x) + (double)(a[i].y - a[j].y) * (a[i].y - a[j].y));
edges.push_back(edge(j, i, cost));
}
}
for (int i = 1; i <= n; i++)
p[i] = i;
for (int i = 1; i <= m; i++){//已经存在的边相当于在为我们初始化并查集
int x, y;
scanf("%d%d", &x, &y);
int rx = find_root(x);
int ry = find_root(y);
if (rx != ry) p[rx] = ry;
}
printf("%.2f\n", kruskal());
return 0;
}
除了kruskal,我们也可以使用prim算法。
在prim算法中,我们把已经存在的边的权值设为0。这样一来,在运行prim算法的时候。假设一条已知边为u,v。在把u加入了已知集合之后,用u更新周围点的mincost,因为u到v的权值是0,所以v到已知集合的最短距离就会更新成0。也就是u,v这条边肯定会被选进去,并且选择的代价是0。完全符合题目的意思!
代码如下:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <queue>
#include <map>
using namespace std;
typedef long long int LL;
const int maxn = 1005;
const int INF = 0x3f3f3f3f;
int n, m;
struct Node{int x, y;};
Node a[maxn];
double G[maxn][maxn], mincost[maxn];
int vis[maxn];
double prim(){
for (int i = 1; i <= n; i++){
mincost[i] = INF;
vis[i] = 0;
}
mincost[1] = 0;
double res = 0;
while (1){
int v;
double m = INF;
for (int i = 1; i <= n; i++)
if (!vis[i] && m > mincost[i]) m = mincost[v = i];
if (m == INF) break;
res += m;
vis[v] = 1;
for (int i = 1; i <= n; i++)//因为任意两个点之间肯定有边,就省去判断是否有边的步骤了
if (mincost[i] > G[v][i]) mincost[i] = G[v][i];
}
return res;
}
int main()
{
//freopen("1.txt", "r", stdin);
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++){
scanf("%d%d", &a[i].x, &a[i].y);
for (int j = 1; j <= i - 1; j++){
double cost = sqrt((double)(a[i].x - a[j].x) * (a[i].x - a[j].x) + (double)(a[i].y - a[j].y) * (a[i].y - a[j].y));
G[i][j] = G[j][i] = cost;
}
}
for (int i = 1; i <= m; i++){
int x, y;
scanf("%d%d", &x, &y);
G[x][y] = G[y][x] = 0;
}
printf("%.2f\n", prim());
return 0;
}