目前还不够全面,将持续更新...
Prim算法:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
//自环和重边(取边权最小值)不会对prim算法产生影响
//对于已经存在的边,将它们的边权赋值为0即可,之后还是按原来的方法求解
int mp[5005][5005], dis[5005];
bool vis[5005];
int n, m, ans;
bool prime()
{
memset(vis, false, sizeof vis);
memset(dis, 0x3f, sizeof dis);
dis[1] = 0;//任取一起点
for(int i = 1; i <= n; i++)
{
int now = -1, _min = 0x3f3f3f3f;
for(int j = 1; j <= n; j++)
{
if(!vis[j] && dis[j] < _min)
{
_min = dis[j];
now = j;
}
}
if(now == -1)
return false;
vis[now] = true;
ans += _min;
//接下来更新不在集合中的各点距离集合的最短距离
for(int j = 1; j <= n; j++)
{
if(!vis[j] && dis[j] > mp[now][j])
dis[j] = mp[now][j];
}
}
return true;
}
signed main()
{
int u, v, w;
cin >> n >> m;
//初始化为无穷大,表示各点之间都独立
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
mp[i][j] = 0x3f3f3f3f;
//建图
for(int i = 1; i <= m; i++)
{
scanf("%d%d%d", &u, &v, &w);
if(!mp[u][v])
mp[u][v] = mp[v][u] = w;
else//去重边
mp[u][v] = mp[v][u] = min(w, mp[u][v]);
}
if(prime())
cout << ans;
else
puts("impossible");
return 0;
}
Kruskal算法:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
//因为使用了并查集,所以自环没有影响,由于进行了排序,所以重边也没影响(优先选取权最小的)
//对于已经存在的边,把权值为0的这些边加入边集数组即可,排序后一定先选它们
int n, m, fa[5005], ans;
struct node
{
int u, v, w;
}edge[200005];
bool cmp(node a, node b)
{
return a.w < b.w;
}
void init()
{
for(int i = 1; i <= n; i++)
fa[i] = i;
}
int find(int x)
{
if(x == fa[x])
return x;
return fa[x] = find(fa[x]);
}
void merge(int x, int y)
{
int fx = find(x), fy = find(y);
if(fx != fy)
fa[fx] = fy;
}
bool kruskal()
{
init();
int t = 0;
sort(edge+1, edge+m+1, cmp);
for(int i = 1; i <= m; i++)
{
if(find(edge[i].u) != find(edge[i].v))
{
t++;
merge(edge[i].u, edge[i].v);
ans += edge[i].w;
}
if(t == n-1)
return true;
}
return false;
}
signed main()
{
cin >> n >> m;
for(int i = 1; i <= m; i++)
scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].w);
if(kruskal())
cout << ans;
else
puts("impossible");
return 0;
}
堆优化的Prim算法:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <utility>
#include <queue>
#define pii pair<int, int>
#define inf 0x3f3f3f3f
using namespace std;
//时间复杂度O(nloge)
//不受自环、重边影响(实验得出)
//适合于边多点少的情况
//dis依然保存到集合的最短距离,通过新加入集合的点来更新
//优先队列可以优化找距离集合最近点的for循环
//链式前向星可以优化更新所有点dis的for循环,因为之前邻接矩阵存图,并不清楚各点邻接关系,需要遍历所有点,而现在只需遍历相邻点
int cnt, n, m, ans, num;
int head[5005], dis[5005];
bool vis[5005];
struct edges
{
int v, w, next;
}edge[400005];//别忘了二倍空间
void init()
{
cnt = 0;
ans = 0;
for(int i = 1; i <= n; i++)
head[i] = 0, dis[i] = inf, vis[i] = false;
}
void add(int u, int v, int w)
{
edge[++cnt].v = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt;
}
bool prime()
{
//在已知边权都为正值时,也可以用大根堆优先队列,只是加入边权时取负数,这样就模拟了小根堆
priority_queue<pii, vector<pii>, greater<pii> > a;
dis[1] = 0;
a.push(make_pair(0, 1));//因为按第一关键词升序排序
while(!a.empty() && num < n)
{
int d = a.top().first;
int now = a.top().second;
a.pop();
if(vis[now])
continue;//一个点可能被加入队列多次,只处理最小的那个,别的都可以跳过
ans += d;
num++;//加入集合的点数
vis[now] = true;//把u加入集合
for(int i = head[now]; i; i = edge[i].next)//每个点的度都要遍历,一共遍历2*e次
{
if(dis[edge[i].v] > edge[i].w)//与朴素算法的不同,这里是用新加入的点去更新邻接的点
{
dis[edge[i].v] = edge[i].w;
a.push(make_pair(dis[edge[i].v], edge[i].v));
}
}
}
if(num == n)
return true;
return false;
}
signed main()
{
int u, v, w;
cin >> n >> m;
init();
for(int i = 1; i <= m; i++)
{
scanf("%d%d%d", &u, &v, &w);
add(u, v, w);
add(v, u, w);
}
if(prime())
cout << ans;
else
puts("impossible");
return 0;
}