最小生成树
最小生成树
1)概念: 在一个无向带权图中,可以联通所有结点并且权值最小的路径就是最小生成树
2)实现算法:
2.1)Kruskal: 将所有边按照权值从大到小排序,然后利用并查集,判断每一条的边的的两个结点是否在同一集合中,不在就合并,并且加上该条边的权值,在就跳过,最后将所有边都处理完就可以得到最小的权值
2.2)Prim: 选一个起始结点,将该节点指向的结点和每条边的权值放入一个小根堆中(按权值建堆),每次去除堆顶的结点并判断该节点是否已被取出过,如果没有就加上这条边的权值,并将该结点的指向的结点加入堆中,不断重复上述步骤,直到堆为空
3)代码实现:
//Kruskal
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
//最小生成树 在无向带权图中,选择一些边,在保证连通性的情况下,总权值最小
// n 个 节点的最小生成树一定有 n-1 条边
//Kruskal算法:将边按权值排序,用并查集判断连接的边是否有环,
//无环就添加,有环就跳过
int n,m;
const int MAX = 100010;
int father[MAX];
vector<pair< pair<int,int>,int > >graph;
bool cmp(pair<pair<int,int>,int>&p1,pair<pair<int,int>,int>&p2){
return p1.second < p2.second;
}
int find(int i){
if(father[i] != i){
father[i] = find(father[i]);
}
return father[i];
}
bool isSame(int a,int b){
return find(a) == find(b);
}
void Union(int a,int b){
int fa = find(a);
int fb = find(b);
if(fa != fb){
father[fa] = fb;
}
}
int main(){
cin >> n >> m;
for(int i = 1;i<=n;i++){
father[i] = i;
}
for(int i = 0;i<m;i++){
int u,v,w;
cin >> u >> v >> w;
graph.push_back({{u,v},w});
}
sort(graph.begin(),graph.end(),cmp);
long long ans = 0;
int cnt = 0;
for(int i = 0;i<m;i++){
int u = graph[i].first.first;
int v = graph[i].first.second;
int w = graph[i].second;
//cout << w << endl;
if(!isSame(u,v)){
Union(u,v);
ans += w;
cnt++;
}
}
if(cnt != n-1){
cout << "orz" << endl;
return 0;
}
cout << ans << endl;
return 0;
}
//Prim
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
int n,m;
const int MAX = 100000;
struct Cmp{
bool operator()(const pair<int,int>&p1, const pair<int,int>&p2){
return p1.second > p2.second;
};
};
bool sset[MAX];
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
priority_queue<pair<int,int>,vector<pair<int,int>>,Cmp>heap;
cin >> n >> m;
vector<vector<pair<int,int>>>graph(n+1);
for(int i = 0;i<m;i++){
int u,v,w;
cin >> u >> v >> w;
graph[u].push_back({v,w});
graph[v].push_back({u,w});
}
sset[1] = true;
for(const auto & edge : graph[1]){
heap.push(edge);
}
int ans = 0;
//集合中节点的数量
int cnt = 1;
while(!heap.empty()){
int nxt = heap.top().first;
int weight = heap.top().second;
heap.pop();
if(!sset[nxt]){
sset[nxt] = true;
ans += weight;
cnt++;
for(const auto &e : graph[nxt]){
heap.push(e);
}
}
}
if(cnt != n){
cout << "orz" <<endl;
return 0;
}
cout << ans << endl;
return 0;
}
Prim还有个优化版,但稍微有点复杂
//Prim improved
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define endl "\n";
const int MAXN = 50010;
const int MAXM = 500010;
//建图
vector<int>head(MAXN);
vector<int>nxt(MAXM);
vector<int>to(MAXM);
vector<int>weight(MAXM);
int cnt;
//建堆
vector<vector<int>>heap(MAXN,vector<int>(2));
int heapsize;
int nodeCnt;
//记录结点索引
vector<int>where(MAXN);
int n,m;
int u,w;
void swap(int i,int j){
int a = heap[i][0];
int b = heap[j][0];
where[a] = j;
where[b] = i;
vector<int>temp = heap[i];
heap[i] = heap[j];
heap[j] = temp;
}
void heapInsert(int i){
while(i!= 0 && heap[i][1] < heap[(i-1)/2][1]){
swap(i,(i-1)/2);
i = (i-1) / 2;
}
}
void heapify(int i){
int l = i * 2 + 1;
while(l<heapsize){
int best = l+1 < heapsize && heap[l+1][1] < heap[l][1] ? l+1 : l;
best = heap[best][1] < heap[i][1] ? best : i;
if(best == i){
break;
}
swap(best,i);
i = best;
l = i * 2 + 1;
}
}
bool isEmpty(){
return heapsize == 0;
}
void pop(){
u = heap[0][0];
w = heap[0][1];
swap(0,--heapsize);
heapify(0);
where[u] = -2;
nodeCnt++;
}
void addEdge(int u,int v,int w){
nxt[cnt] = head[u];
head[u] = cnt;
to[cnt] = v;
weight[cnt] = w;
cnt++;
}
void addOrUpdateOrIngore(int ei){
int v = to[ei];
int w = weight[ei];
if(where[v] == -1){
heap[heapsize][0] = v;
heap[heapsize][1] = w;
where[v] = heapsize++;
heapInsert(where[v]);
}
else if (where[v] >= 0){
heap[where[v]][1] = min(heap[where[v]][1],w);
heapInsert(where[v]);
}
// cout << "v: " << v << endl;
// cout << "where[v] :" << where[v] << endl;
// cout << "addORUp: prnit" << " " << heapsize << endl;
}
void build(){
cnt = 1;
heapsize = 0;
nodeCnt = 0;
for(int i = 0;i<=n;i++){
where[i] = -1;
}
for(int i = 0;i<=n;i++){
head[i] = 0;
}
}
int prim(){
nodeCnt = 1;
where[1] = -2;
for(int ei = head[1];ei > 0;ei = nxt[ei]){
addOrUpdateOrIngore(ei);
}
int ans = 0;
while(!isEmpty()){
pop();
ans += w;
for(int ei = head[u];ei > 0;ei = nxt[ei]){
addOrUpdateOrIngore(ei);
}
}
return ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin >> n >> m;
build();
for(int i = 0;i<m;i++){
int t1,t2,t3;
cin >> t1 >> t2 >> t3;
addEdge(t1,t2,t3);
addEdge(t2,t1,t3);
}
int ans = prim();
if(nodeCnt != n){
cout << "orz" << endl;
}
else {
cout << ans << endl;
}
// cout << nodeCnt << endl;
// cout << ans << endl;
return 0;
}
浙公网安备 33010602011771号