方法总结:
1 朴素prim 算法,邻接表
2 朴素prim 算法,邻接矩阵
3 堆优化prim 算法,邻接表
4 堆优化prim 算法,邻接矩阵
5 Kruskal 算法
堆优化感觉,更适合邻接表,因为在邻接矩阵里,还是得遍历n次,即每个顶点,
然后堆优化的优势就没有完全发挥出来来。
prim 算法 前置知识,贪心、dp,dijsktra算法
prim算法和dijsktra算法的区别就是
dijsktra算法 的松弛是 dist[to] = dist[from] + graph[from][to]
prim算法 的松弛是 dist[to] = graph[from][to]
相同点:都是先从最小的dist[i] 开始,然后bfs,去松弛边
kruskal 算法,前置知识,并查集,贪心算法。
贪心的思想就是,尽量使经过的边要最小。
表现在prim算法里,就是,每次都是去找边权最小的那条边
表现在kruskal 算法,就排序贪心,以边权为排序变量,从小到大排序
然后,逐个合并连通块。
1 朴素 Prim 算法 邻接表
#include<iostream>
#include<vector>
#include<cstring>
#include<set>
using namespace std;
using PII = pair<int,int>;
const int MAXN = 105;
const int INF = 0x3f3f3f3f;
struct Node{
int from,to;
int dis;
};
vector<Node> graph[MAXN];
set<PII> paths;
bool vis[MAXN];
int dist[MAXN];
int froms[MAXN];
int n,m;
bool prim(int start){
memset(dist,0x3f,sizeof dist);
dist[start] = 0;
for(int i = 1;i <= n;i++){
int minId = -1;
int minDis = INF;
for(int j = 1;j <= n;j++){
if(!vis[j] && dist[j] < minDis){
minDis = dist[j];
minId = j;
}
}
vis[minId] = true;
if(froms[minId]){
int f = min(froms[minId],minId);
int t = max(froms[minId],minId);
paths.insert({f,t});
}
for(auto node : graph[minId]){
int to = node.to;
int d = node.dis;
if(!vis[to] && dist[to] > d){
dist[to] = d;
froms[to] = minId;
}
}
}
return true;
}
int main(){
cin >> n >> m;
for(int i = 1;i <= m;i++){
int from,to,d;
cin >> from >> to >> d;
graph[from].push_back({from,to,d});
graph[to].push_back({to,from,d});
}
prim(1);
for(auto pii : paths){
cout << pii.first <<' ' << pii.second << '\n';
}
return 0;
}
2 朴素 prim 算法,邻接矩阵
#include<iostream>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
using PII = pair<int,int>;
const int MAXN = 105;
const int INF = 0x3f3f3f3f;
int graph[MAXN][MAXN];
int dist[MAXN];
int froms[MAXN];
bool vis[MAXN];
set<PII> paths;
int n,m;
void prim(int start){
memset(dist,0x3f,sizeof dist);
dist[start] = 0;
for(int i = 1;i <= n;i++){
int minId = -1;
int minDis = INF;
for(int j = 1;j <= n;j++){
if(!vis[j] && dist[j] < minDis){
minDis = dist[j];
minId = j;
}
}
vis[minId] = true;
if(froms[minId]){
int f = min(froms[minId],minId);
int t = max(froms[minId],minId);
paths.insert({f,t});
}
for(int j = 1;j <= n;j++){
if(!vis[j] && dist[j] > graph[minId][j]){
dist[j] = graph[minId][j];
froms[j] = minId;
}
}
}
}
int main(){
memset(graph,0x3f,sizeof graph);
cin >> n >> m;
for(int i = 1;i <= m;i++){
int from,to,dis;
cin >> from >> to >> dis;
graph[from][to] = dis;
graph[to][from] = dis;
}
prim(1);
for(auto p : paths){
cout << p.first <<' ' << p.second << '\n';
}
return 0;
}
3 堆优化 prim 算法 ,邻接表
#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<set>
#include<cstring>
using namespace std;
using PII = pair<int,int>;
const int MAXN = 105;
const int INF = 0x3f3f3f3f;
struct Node{
int to;
int weight;
bool operator < (const Node& other)const{
return weight > other.weight;
}
};
vector<Node> graph[MAXN];
int dist[MAXN];
bool vis[MAXN];
int froms[MAXN];
priority_queue<Node> q;
set<PII> paths;
int n,m;
void prim(int start){
memset(dist,0x3f,sizeof dist);
dist[start] = 0;
q.push({start,0});
int visCnt = 0;
while(!q.empty()){
auto cur = q.top();
q.pop();
if(vis[cur.to]){
continue;
}
vis[cur.to] = true;
if(froms[cur.to]){
int f = min(froms[cur.to],cur.to);
int t = max(froms[cur.to],cur.to);
paths.insert({f,t});
}
if(++visCnt == n){
break;
}
for(auto ne : graph[cur.to]){
int to = ne.to;
int w = ne.weight;
if(!vis[to] && w < dist[to]){
dist[to] = w;
froms[to] = cur.to;
q.push(ne);
}
}
}
}
int main(){
cin >> n >> m;
for(int i = 1;i <= m;i++){
int from,to,weight;
cin >> from >> to >> weight;
graph[from].push_back({to,weight});
graph[to].push_back({from,weight});
}
prim(1);
for(auto p : paths){
cout << p.first << ' ' << p.second << endl;
}
return 0;
}
4 堆优化 prim 算法,邻接矩阵
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<set>
using namespace std;
using PII = pair<int,int>;
const int MAXN = 105;
const int INF = 0x3f3f3f3f;
int graph[MAXN][MAXN];
int dist[MAXN];
bool vis[MAXN];
int froms[MAXN];
struct Node{
int to;
int weight;
bool operator < (const Node& other)const{
return weight > other.weight;
}
};
priority_queue<Node> q;
set<PII> paths;
int n,m;
void prim(int start){
memset(dist,0x3f,sizeof dist);
dist[start] = 0;
q.push({start,0});
while(!q.empty()){
auto cur = q.top();
q.pop();
if(vis[cur.to]){
continue;
}
vis[cur.to] = true;
if(froms[cur.to]){
int f = min(cur.to,froms[cur.to]);
int t = max(cur.to,froms[cur.to]);
paths.insert({f,t});
}
for(int j = 1;j <= n;j++){
if(!vis[j] && graph[cur.to][j] < dist[j]){
dist[j] = graph[cur.to][j];
froms[j] = cur.to;
q.push({j,dist[j]});
}
}
}
}
int main(){
cin >> n >> m;
memset(graph,0x3f,sizeof graph);
for(int i = 1;i <= m;i++){
int from,to,weight;
cin >> from >> to >> weight;
graph[from][to] = weight;
graph[to][from] = weight;
}
prim(1);
for(auto p : paths){
cout << p.first << ' ' << p.second << endl;
}
return 0;
}
5 Kruskal 算法
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
using PII = pair<int,int>;
const int MAXN = 105;
const int MAXM = MAXN * MAXN;
struct Edge{
int from,to;
int weight;
};
Edge edges[MAXM];
vector<Edge> paths;
int fa[MAXN];
int n,m;
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]);
}
bool cmpEdge(const Edge& a,const Edge& b){
return a.weight < b.weight;
}
bool cmpSet(const Edge& a,const Edge& b){
if(a.from == b.from){
return a.to < b.to;
}
return a.from < b.from;
}
int main(){
cin >> n >> m;
init();
for(int i = 1;i <= m;i++){
int from,to,weight;
cin >> from >> to >> weight;
if(from > to){
swap(from,to);
}
edges[i] = {from,to,weight};
}
sort(edges + 1,edges + m + 1,cmpEdge);
for(int i = 1;i <= m;i++){
int a = edges[i].from;
int b = edges[i].to;
int pa = find(a);
int pb = find(b);
if(pa != pb){
fa[pa] = pb;
paths.push_back(edges[i]);
}
}
sort(paths.begin(),paths.end(),cmpSet);
for(auto p : paths){
cout << p.from << ' ' << p.to << endl;
}
return 0;
}