最小生成树(MST):将给出的所有点连接起来(即从一个点可到任意一个点),且连接路径之和最小的图叫最小生成树。
数据结构:树形结构,或者说是直链型结构,因为当n个点相连,且路径和最短,那么将它们相连的路一定是n-1条
实现思路:将点分为在树中的点与不在树中的点,每次取出树中点的连接的最小路径,且该路径连接的点不在树中,然后将该路径连接的点加入树中,重复并进行路径更新,即松弛,当取出边达到n-1条时,树已建立。
洛谷P1546
求一条路径,使得u到v连通的路径的最小长度
(Prim算法)
本质上还是贪心思想。
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=110;
const int Inf=0x3f3f3f3f;
int n;
int adj[maxn][maxn];
int cost[maxn],vis[maxn];
int prim(){
for(int i=0;i<=n;i++){
cost[i]=Inf;//0-n的花费都为无穷大
vis[i]=0;//所有点都还没在集合里
}
cost[1]=0;//将1号点加入集合
int res=0;//保存答案
for(int i=1;i<=n;i++){
int minn=0;//保存花费最小的下标
for(int j=1;j<=n;j++){
if(vis[j]==0&&cost[j]<cost[minn]){
minn=j;//每次都要一个代价最小的
}
}
vis[minn]=1;//将点加入集合
res+=cost[minn];//代价及时加上去
for(int i=1;i<=n;i++){//从选择的点出发,遍历所有的点,看看那些点的代价可以被改小
if(!vis[i]&&cost[i]>adj[minn][i]){
cost[i]=adj[minn][i];
}
}
}
return res;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>adj[i][j];
cout<<prim()<<endl;
}
Kruskal算法:还是贪心思想。(sort+并查集)
#include<iostream>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=5005;
const int maxm=200205;
int father[maxn];
int n,m;
int mst;
struct node{
int u,v,w;
bool operator<(const node & n)const{
return w<n.w;//价值从小到大
}
}g[maxm];
int find(int x){//找父亲结点。
if(x==father[x])return x;
return father[x]=find(father[x]);//路径压缩,把递归过程中遇到的
//结点的祖宗结点也直接修改了。
}
void merge(int v,int u){
father[find(v)]=find(u);//合并v,w.
}
bool k(){
for(int i=1;i<=n;i++){
father[i]=i;
}
int cnt;
cnt=mst=0;
sort(g,g+m);
for(int i=0;i<m;i++){
int u=g[i].u;
int v=g[i].v;
int w=g[i].w;
if(find(u)!=find(v)){
mst+=w;
cnt++;
merge(u,v);
if(cnt==n-1)return true;
}
}
return false;
}
int main(){
cin>>n>>m;
for(int i=0;i<m;i++){
cin>>g[i].u>>g[i].v>>g[i].w;
}
if(k()){
cout<<mst<<endl;
}
else cout<<"orz"<<endl;
return 0;
}