最小生成树
基础思路:这一part主要有两个算法,prim和kruskal,两者都是采用的贪心(p是贪最近的新点,k是贪最小的新边)
1、prim(每次拿一个新的节点,并用该节点辐射所有未用过的点把它们之间的距离更新进dis,然后在下一轮中遍历所有这些边,取最近的新点):
`
const int NUM = 100010;
int map[510][510];
int state[510];int dis[510];int n, m;
void prim() {
memset(dis, 0x3f3f3f3f, sizeof(dis));
dis[1] = 0;//这个起点是可以任取的,一般是1
int res = 0;//树的所有边之和
for (int i1 = 1; i1 <= n; i1++) {
int upoi = 0;
for (int j = 1; j <= n; j++) {
if (state[j] == 0 && (upoi == 0 || dis[upoi] > dis[j])) {
upoi = j;//寻找第i1次循环中所有可选连通点中权值最小者并更新为子树新节点
}
}
if (dis[upoi] == 0x3f3f3f3f) {
cout << "impossible";//如果目前新节点没有连接的新节点,那么这棵树就生成不下去了,此处如无需要可删
return;
}
res += dis[upoi];
state[upoi] = 1;//处理更新后的upoi为子树的新节点
for (int j1 = 1; j1 <= n; j1++) {
dis[j1] = min(dis[j1], map[upoi][j1]);//处理子树新节点与整个图所有联通点的距离,如果有距离(小于0x3f3f3f3f),则更新进dis
//***注解:与dijkstra算法的核心区别,dis维护的是该点j1到生成树的最短距离。
//Q:怎么维护到一棵树的最短距离?A:在此步骤之前,dis已经依次维护过了j1与生成树中其他所有节点的最短距离了,此时又进来一个新的,所以更新一下可能的更小点。所以是到树的最短距离
}
}
cout << res << endl;
}
int main() {
cin >> n >> m;
memset(map, 0x3f3f3f3f, sizeof(map));//找到新点之后会遍历新点和所有点的map距离,所以如果没有读入,要默认为极大值,不然影响最小值的判断
for (int i = 1; i <= m; i++) {
int a, b, w;
cin >> a >> b >> w;
if (a > b) { swap(a, b); }
map[a][b] = map[b][a] = min(map[a][b],w);//无向图
//一定要注意处理重边!!!
}
prim();
return 0;
}`
复杂度为O(n^2),适用稠密图。
2、kruskal(将所有边按照它们边权值从小到大排序,每次取出一条边判断,如果会和之前的边组成回路就舍去,是否回路通过点的并查集来知晓。且如果有n个点,若最后cnt筛选出n-1条边,则生成树存在)
`
const int NUM=200010;
int p[NUM];
int res=0;int n,m;int cnt=0;
struct Line{
int u,v,w;
}line[NUM];
bool cmp(const Line&i,const Line&j){
if (i.w<j.w){
return true;
}else{
return false;
}
}
int find(int a){
if (p[a]!=a){
p[a]=find(p[a]);
}
return p[a];//并查集模板
}
void kruskal(){
for (int i=1;i<=m;i++){
int pu=find(line[i].u);
int pv=find(line[i].v);//判断该边两个点是否已经在一起,如果在则加入该边会导致回路
if (pu!=pv){
p[pv]=pu;
res+=line[i].w;//记录最小生成树的总权值
cnt++;//记录一共选了几条边
}
}
}
int main(){
cin>>n>>m;
for (int i=1;i<=m;i++){
cin>>line[i].u>>line[i].v>>line[i].w;
}
for (int k=1;k<=n;k++){
p[k]=k;
}
sort(line+1,line+m+1,cmp);
kruskal();
if (cnt<n-1){
cout<<"impossible";//如果选出的边少于n-1是不可能构成一棵树的
}else{
cout<<res;
}
return 0;
}`
该算法适用稀疏图。
关于稠密:设边数E,点数V,当E的数量级在 𝑉log𝑉以上、接近 𝑉^2时,prim效果好,低于VlogV,kruskal更好