首先,我们来看看这两种算法的效率,当然,prim和Dijkstra算法有异曲同工之妙,既然Dijkstra能用堆优化,prim当然也可以。
以下测试数据转自http://blog.csdn.net/gykimo/article/details/8538275
评测环境:WindowsXP,FreePascal2.40,Pentium(R) Dual-Core CPU T4300@2.10GHz,2G内存
通过上图可以看出:
1.Prim在稠密图中比Kruskal优,在稀疏图中比Kruskal劣。
2.Prim+Heap在任何时候都有令人满意的的时间复杂度,但是代价是空间消耗极大。【以及代码很复杂>_<】
3.时间复杂度并不能反映出一个算法的实际优劣。
竞赛所给的题大多数是稀疏图,所以尽可能地使用Prim+Heap吧,在稀疏图中这是无敌的。如果一定要在朴素Prim和Kruskal里选一个的话那就用Kruskal吧。当然Prim的代码比较简单,对付水题用Prim也无所谓,只要不是极稀疏图两者相差不大
1.Prim在稠密图中比Kruskal优,在稀疏图中比Kruskal劣。
2.Prim+Heap在任何时候都有令人满意的的时间复杂度,但是代价是空间消耗极大。【以及代码很复杂>_<】
3.时间复杂度并不能反映出一个算法的实际优劣。
竞赛所给的题大多数是稀疏图,所以尽可能地使用Prim+Heap吧,在稀疏图中这是无敌的。如果一定要在朴素Prim和Kruskal里选一个的话那就用Kruskal吧。当然Prim的代码比较简单,对付水题用Prim也无所谓,只要不是极稀疏图两者相差不大
一、prim
这里记顶点数v,边数e
邻接矩阵:O(v2) 邻接表:O(elog2v)
代码实现(来自POJ1789):
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char a[23333][10];
int d[233333];
int n;
bool used[23333];
int map[2005][2005];
int prim()
{
memset(used, 0, sizeof(used));
memset(d, 0x3f, sizeof(d));
d[1] = 0;
int res = 0;
while(1)
{
int v = -1;
for(int u = 1;u <= n;u ++)
{
if(!used[u] && (v == -1|| d[u] < d[v]))
v = u;
}
if(v == -1)
break;
used[v] = 1;
res += d[v];
for(int u = 1;u <= n;u ++)
{
d[u] = min(d[u], map[v][u]);
}
}
return res;
}
int main()
{
while(scanf("%d", &n)!=EOF)
{
if(n == 0)
break;
for(int i = 1;i <= n;i ++)
scanf("%s", a[i]+1);
int ans = 0;
for(int i = 1;i <= n;i ++)
{
for(int k = i;k <= n;k ++)
{
ans = 0;
if(k == i)
continue;
for(int j = 1;j <= 7;j ++)
{
if(a[i][j] != a[k][j])
{
ans ++;
}
}
map[i][k] = ans;
map[k][i] = ans;
}
}
printf("The highest possible quality is 1/%d.\n", prim());
}
return 0;
}
二、prim+heap
与上面的思想一样,只是我用一个优先队列去维护一下,就可以加快速度了。
codevs1231 最优布线问题亲测 kruskal 179ms prim+heap 123ms
再加上本文开头转载的图片可以看出,prim+heap是很吊的,尤其是对于稠密图,例如poj 1789 Truck History就只能用prim做,因为边数实在太多。
代码实现(基于codevs1231)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int MAXN = 1e5+5;
int n, m;
int first[MAXN], next[MAXN << 1], tot = 0;
bool used[MAXN];
struct edge
{
int from, to, cost;
bool operator < (const edge &b)const
{
return cost > b.cost;
}
}es[MAXN << 1];
priority_queue <edge> q;
void init()
{
memset(first, -1, sizeof(first));
tot = 0;
}
void build(int f, int t, int d)
{
es[++tot] = (edge){f, t, d};
next[tot] = first[f];
first[f] = tot;
}
long long prim(int s)
{
memset(used, 0, sizeof(used));
long long ans = 0;
int now = s;
used[s] = 1;
for (int i = first[s]; i != -1; i = next[i])
q.push(es[i]);
while (!q.empty())
{
edge u = q.top();
q.pop();
int v = u.to;
if (used[v])
continue;
ans += u.cost;
used[v] = 1;
for (int i = first[v]; i != -1; i = next[i])
{
if (!used[es[i].to])
{
q.push(es[i]);
}
}
}
return ans;
}
int main()
{
init();
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
{
int f, t, d;
scanf("%d%d%d", &f, &t, &d);
build(f, t, d);
build(t, f, d);
}
printf("%lld", prim(1));
return 0;
}
三、Kruskal
kruskal是对所有边进行排序,然后选权值最小的边连接,然后用并查集进行维护,就可以在O(eloge)的时间内求解,e为边数,所以当边数很大时会很慢。
代码实现(基于codevs1231)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int fa[1000005], tot = 0;
struct edge
{
int from, to, cost;
}es[233333];
void build(int f, int t, int d)
{
es[++tot] = (edge){f, t, d};
}
int find(int x)
{
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
bool cmp(edge a, edge b)
{
return a.cost < b.cost;
}
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int f, t, d;
scanf("%d%d%d", &f, &t, &d);
build(f, t, d);
build(t, f, d);
}
sort(es + 1, es + tot + 1, cmp);
for (int i = 1; i <= tot; i++)
fa[i] = i;
long long ans = 0;
for (int i = 1; i <= tot; i++)
{
int x = es[i].from, y = es[i].to;
if (find(x) != find(y))
{
fa[find(x)] = find(y);
ans += es[i].cost;
}
}
cout << ans;
return 0;
}







