最小生成树

最小生成树(练习总结)

一.首先是其版子

P3366 【模板】最小生成树

 代码+注释

#include<bits/stdc++.h>
using namespace std;
struct woyaohongming{ //不要在意这个结构体的名字......它只是用来存储图的而已
    int s,e,w;    //s-start,e-end,代表边上的两个节点,w就是权值费用了
}a[200005];
int f[200005];  //并查集用的f数组
bool cmp(woyaohongming a,woyaohongming b){   //sort排序规则,按费用从低到高排序
    return a.w<b.w;
}
int find(int a){  //并查集的“找祖宗”函数,注意别忘路径压缩
    if(f[a]==a)
    return a;
    else return f[a]=find(f[a]);
}
int main(){
    int n,m,k;
    cin>>n>>m>>k;   //输入n、m、k,没啥好讲的
    for(int i=1;i<=n;i++)   
    f[i]=i;  //并查集数组初始化,每个节点的祖宗一开始是它自己
    for(int i=1;i<=m;i++)
    cin>>a[i].s>>a[i].e>>a[i].w;  //输入图的信息
    sort(a+1,a+1+m,cmp);   //快活的按权值排个序
   int cnt=0,sum=0;  //cnt是已经选中的边数,sum是最终要输出的最小权值
    for(int i=1;i<=m;i++){   //m条边,循环m次
        if(find(a[i].s)!=find(a[i].e)){ //如果俩节点的祖宗不相等(也就是不是回路),就可以加进去
            f[find(a[i].s)]=find(a[i].e); //把它俩合并成一个祖宗
            sum+=a[i].w;    //更新最小费用
            cnt++; //边数+1
        }
        if(cnt>=n-k)  //边数到达(n-k)条边,任务完成,break
        break;
    }
    if(cnt>=n-k)   //如果选了n-k条边,可以搞最小生成树
    cout<<sum;  //输出最小权值
    else cout<<"No Answer";  //要不然选了m条边还都搞不好,不能构成最小生成树
    return 0;  
} 

二.再来一点变形简单题

1.有指定范围的树生成

P1396 营救

此题目与版子的区别就是需要多加一步判断,在kruskal的同时判断开始节点和结束节点是否已经在同一颗树内。
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int  f[30005],n,m,s,t;
 4 struct edge{
 5     int u,v,w;
 6 }e[30005];
 7 bool cmp(edge x,edge y){
 8     return x.w <y.w ;
 9 }
10 int find(int x){
11     if(f[x]==x)return x;
12     else return f[x]=find(f[x]);
13 }
14 void kruskal(){
15     sort(e+1,e+m+1,cmp);
16     for(int i=1;i<=m;i++){
17         int u=find(e[i].u ) ,v=find(e[i].v );
18         f[u]=v;
19         if(find(s)==find(t)){//判断是否有公共祖先,如果有就证明此时为最小拥堵值 
20             cout<<e[i].w;
21             return ;
22         }
23     }
24 }
25 int main(){
26     cin>>n>>m>>s>>t;
27     for(int i=1;i<=n;i++)f[i]=i;
28     for(int i=1;i<=m;i++){
29         cin>>e[i].u >>e[i].v >>e[i].w ;
30     }
31     kruskal();
32 return 0;
33 }

2.指定生成树节点的个数

P1195 口袋的天空 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

想要 k 个点,并且联通,所以至少需要 k-1 条边。所以此生成器的结束条件为 cnt 是否达到 n-k 个。

 1 #include<bits/stdc++.h>
 2 //要想要k个点,就必须达到k-1条边 
 3 using namespace std;
 4 int n,m,k,num,cnt;
 5 int f[100005];
 6 struct edge{
 7     int u,v,w;
 8 }e[10005];
 9 bool cmp(edge x,edge y){
10     return x.w <y.w ;
11 } 
12 int find(int x){
13     if(f[x]==x)return x;
14     else return f[x]=find(f[x]);
15 }
16 void kruskal(){
17     sort(e+1,e+m+1,cmp);
18     for(int i=1;i<=m;i++){
19         int u=find(e[i].u ) ,v=find(e[i].v );
20         if(u==v)continue;
21         f[u]=v; 
22         cnt++;
23         num+=e[i].w ;
24         if(cnt>=n-k){
25             cout<<num;return ;//注意此时正向判断时cnt的判断“是”放在 大括号内 
26         }//
27     }//
28     cout<<"No Answer";return ;//“否”放在大括号外 
29 }
30 int main(){
31     cin>>n>>m>>k;
32     for(int i=1;i<=m;i++){
33         cin>>e[i].u >>e[i].v >>e[i].w ;
34     } 
35     for(int i=1;i<=n;i++)f[i]=i;
36     kruskal();
37 
38 return 0;
39 }

3.易错点(是否为公共祖先的正负向判断)

结果为“是”时,if正向判断在内,负向在外。

结果为“否”时,全部在内。

P2330 [SCOI2005] 繁忙的都市 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int  f[30005],n,m,s,t,num,cnt;
 4 struct edge{
 5     int u,v,w;
 6 }e[30005];
 7 bool cmp(edge x,edge y){
 8     return x.w <y.w ;
 9 }
10 int find(int x){
11     if(f[x]==x)return x;
12     else return f[x]=find(f[x]);
13 }
14 void kruskal(){
15     sort(e+1,e+m+1,cmp);
16     for(int i=1;i<=m;i++){
17         if(find(e[i].u )!=find(e[i].v )){
18             int u=find(e[i].u ) ,v=find(e[i].v );
19             f[u]=v;cnt++;num=e[i].w ;
20         }
21         if(cnt==n-1)break;
22     }
23 }
24 int main(){
25     cin>>n>>m;
26     for(int i=1;i<=n;i++)f[i]=i;
27     for(int i=1;i<=m;i++){
28         cin>>e[i].u >>e[i].v >>e[i].w ;
29     }
30     kruskal();
31     cout<<n-1<<" "<<num;
32 return 0;
33 }

 

P1547 [USACO05MAR] Out of Hay S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int  f[30005],n,m,s,t,num,cnt;
 4 struct edge{
 5     int u,v,w;
 6 }e[30005];
 7 bool cmp(edge x,edge y){
 8     return x.w <y.w ;
 9 }
10 int find(int x){
11     if(f[x]==x)return x;
12     else return f[x]=find(f[x]);
13 }
14 void kruskal(){
15     sort(e+1,e+m+1,cmp);
16     for(int i=1;i<=m;i++){
17         if(find(e[i].u )==find(e[i].v ))continue;
18             int u=find(e[i].u ),v=find(e[i].v );
19             f[u]=v;
20             cnt++;
21             num=e[i].w ;
22         
23         if(cnt==n-1)break;
24     }
25 }
26 int main(){
27     cin>>n>>m;
28     for(int i=1;i<=n;i++)f[i]=i;
29     for(int i=1;i<=m;i++){
30         cin>>e[i].u >>e[i].v >>e[i].w ;
31     }
32     kruskal();
33     cout<<num;
34 return 0;
35 }

 

 
posted @ 2024-02-14 23:38  晓屿  阅读(29)  评论(0)    收藏  举报