SDUTOJ 2021级ACM班&2022年寒假集训《数据结构》专题11--最小生成树、并查集

A - 小雷的冰茶几(——5.5)

题目链接 https://acm.sdut.edu.cn/onlinejudge3/contests/3989/problems/A

并查集。一共需要搬动的次数,即,一共有几个集合。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int t,n,k,x,y;
 4 int p[100010];
 5 
 6 void Init(int n)
 7 {
 8     for(int o=1; o<=n; o++)
 9         p[o]=o;
10 }
11 
12 int Getp(int o)
13 {
14    return p[o]==o?o:(p[o]=Getp(p[o]));
15 }
16 
17 void Union(int x,int y)
18 {
19     int x_root=Getp(x);
20     int y_root=Getp(y);
21     if(x_root!=y_root)
22         p[y_root]=x_root;
23     return;
24 }
25 
26 int main()
27 {
28     cin>>t;
29     for(int i=1; i<=t; i++)
30     {
31         int cnt=0;
32         cin>>n>>k;
33         Init(n);
34         for(int j=1; j<=k; j++)
35         {
36             cin>>x>>y;
37             Union(x,y);
38         }
39         for(int k=1; k<=n; k++)
40         {
41             if(p[k]==k)
42                 cnt++;
43         }
44         printf("Case %d: %d\n",i,cnt);
45     }
46     return 0;
47 }

 

B - 电影节(更于——5.6)

题目链接 https://acm.sdut.edu.cn/onlinejudge3/contests/3989/problems/B

并查集模板

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,m,a,b,c,d;
 4 int p[100010];
 5 
 6 void init(int n)
 7 {
 8     for(int o=1; o<=n; o++)
 9         p[o]=o;
10 }
11 
12 int getp(int o)
13 {
14     return p[o]==o?o:(p[o]=getp(p[o]));
15 }
16 
17 void Union(int x,int y)
18 {
19     int x_root=getp(x);
20     int y_root=getp(y);
21     if(x_root!=y_root)
22         p[y_root]=x_root;
23     return;
24 }
25 
26 int main()
27 {
28     while(cin>>n>>m)
29     {
30         init(n);
31         for(int i=1; i<=m; i++)
32         {
33             cin>>a>>b;
34             Union(a,b);
35         }
36         cin>>c>>d;
37         if(getp(c)==getp(d)) cout<<"same"<<endl;
38         else cout<<"not sure"<<endl;
39     }
40     return 0;
41 }

 

C - 小鑫的城堡

题目链接 https://acm.sdut.edu.cn/onlinejudge3/contests/3989/problems/C

看到图以为是最小生成树或者最短路径,但是样例只有两列,寻思着或许是最短路计数这样的?结果又是一道并查集...

(喂,七道题,已经三道并查集了!)

把输入的每行的两个点(房间)放入并查集,如果两个点的根节点相同则构成回路,不符合小鑫的想法。

输入要求中的 m 为边数,因此点的数目应该为 m+1。

 1 #include<bits/stdc++.h>
 2 #define maxm 100010
 3 using namespace std;
 4 int m,x,y;
 5 int p[maxm];
 6 int vis[maxm];
 7 
 8 void init()
 9 {
10     for(int o=1; o<=maxm; o++)
11         p[o]=o;
12 }
13 
14 int getp(int o)
15 {
16     return p[o]==o?o:(getp(p[o]));
17 }
18 
19 int main()
20 {
21     while(cin>>m)
22     {
23         init();
24         memset(vis,0,sizeof(vis));
25         int flag=0;
26         int sum=0;
27         for(int i=1; i<=m; i++)
28         {
29             cin>>x>>y;
30             vis[x]=1;
31             vis[y]=1;
32             int x_root=getp(x);
33             int y_root=getp(y);
34 
35             if(x_root!=y_root) p[y_root]=x_root;
36             else flag=1;
37         }
38         for(int j=1; j<=maxm; j++) if(vis[j]==1) sum++;
39 
40         if(flag==0 && sum==m+1) cout<<"Yes"<<endl;
41         else cout<<"No"<<endl;
42     }
43     return 0;
44 }

 

D - 数据结构实验:连通分量个数

题目链接 https://acm.sdut.edu.cn/onlinejudge3/contests/3989/problems/D

本人表示很。。无雨。。专题11共7个题4个并查集了。。

而且下面这题跟A那个不能说是一模一样吧,也就是输出不同。。。。

我:&*%¥@*#。。。。。。

 1 #include<bits/stdc++.h>
 2 #define maxn 1010
 3 #define maxm 10010
 4 using namespace std;
 5 int t,n,m,u,v;
 6 int p[maxn];
 7 
 8 void init(int n)
 9 {
10     for(int i=1; i<=n; i++)
11         p[i]=i;
12 }
13 
14 int getp(int o)
15 {
16     return p[o]==o?o:(getp(p[o]));
17 }
18 
19 void unio(int u,int v)
20 {
21     int x=getp(u);
22     int y=getp(v);
23     if(x!=y) p[y]=x;
24 }
25 
26 int main()
27 {
28     cin>>t;
29     while(t--)
30     {
31         cin>>n>>m;
32         init(n);
33         int sum=0;
34         for(int i=1; i<=m; i++)
35         {
36             cin>>u>>v;
37             unio(u,v);
38         }
39         for(int j=1; j<=n; j++)
40         {
41             if(p[j]==j)
42                 sum++;
43         }
44         cout<<sum<<endl;
45     }
46     return 0;
47 }

 

E - 数据结构实验之图论九:最小生成树

题目链接 https://acm.sdut.edu.cn/onlinejudge3/contests/3989/problems/E

板子

kruskal

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,m,cnt,ans;
 4 int p[105];
 5 
 6 struct node
 7 {
 8     int u,v,w;
 9 }edge[10010];
10 
11 bool cmp(node a,node b)
12 {
13     return a.w<b.w;
14 }
15 
16 int getp(int o)
17 {
18     return p[o]==o?o:getp(p[o]);
19 }
20 
21 int kruskal()
22 {
23     for(int i=1; i<=n; i++)
24         p[i]=i;
25     sort(edge+1,edge+m+1,cmp);
26     for(int i=1; i<=m; i++)
27     {
28         int x=getp(edge[i].u);
29         int y=getp(edge[i].v);
30         if(x==y) continue;
31         ans+=edge[i].w;
32         p[y]=x;
33         cnt++;
34         if(cnt==n-1) break;
35     }
36     return ans;
37 }
38 
39 int main()
40 {
41     ios::sync_with_stdio(false);
42     while(cin>>n>>m)
43     {
44         cnt=0;
45         ans=0;
46         for(int i=1; i<=m; i++)
47             cin>>edge[i].u>>edge[i].v>>edge[i].w;
48         kruskal();
49         if(cnt==n-1) cout<<ans<<endl;
50         else cout<<"0"<<endl;
51     }
52     return 0;
53 }

用链式前向星做的朴素版primTLE了?。。。

啊?1999ms?啊?好吧。。。不用你了。

 1 #include<bits/stdc++.h>
 2 #define INF 99999
 3 using namespace std;
 4 int n,m,u,v,w,cnt,tot,ans;
 5 int head[10100];
 6 int dis[10100];
 7 bool vis[105];
 8 
 9 struct node
10 {
11     int v,w,next;
12 } edge[20100];
13 
14 void add(int u,int v,int w)
15 {
16     edge[++tot].v=v;
17     edge[tot].w=w;
18     edge[tot].next=head[u];
19     head[u]=tot;
20 }
21 
22 int prim()
23 {
24     int now=1;
25     for(int i=2; i<=n; i++)
26         dis[i]=INF;
27     for(int i=head[1]; i!=0; i=edge[i].next)
28         dis[edge[i].v]=min(dis[edge[i].v],edge[i].w);
29     while(++cnt<n)
30     {
31         int minn=INF;
32         vis[now]=1;
33         for(int i=1; i<=n; i++)
34         {
35             if(!vis[i] && dis[i]<minn)
36             {
37                 minn=dis[i];
38                 now=i;
39             }
40         }
41         ans+=minn;
42         if(minn==INF) break;
43         for(int i=head[now]; i!=0; i=edge[i].next)
44         {
45             int v=edge[i].v;
46             if(!vis[v] && dis[v]>edge[i].w)
47                 dis[v]=edge[i].w;
48         }
49     }
50     return ans;
51 }
52 
53 int main()
54 {
55     ios::sync_with_stdio(false);
56     cin.tie(NULL);
57     while(cin>>n>>m)
58     {
59         cnt=0;
60         ans=0;
61         if(m!=0)
62         {
63             for(int i=1; i<=m; i++)
64             {
65                 cin>>u>>v>>w;
66                 add(u,v,w);
67                 add(v,u,w);
68             }
69             prim();
70             if(cnt>=n-1) cout<<ans<<endl;
71         }
72         else cout<<"0"<<endl;
73     }
74     return 0;
75 }

 

 

F - 人活着系列之寻找最完美的人生

题目链接 https://acm.sdut.edu.cn/onlinejudge3/contests/3989/problems/F

这题有点儿怪..没给我边的权值,给了我两个点的...“点值?”,叫我自己算。

平常喜欢用链式前向星写,但是这题确实叫我不知道怎么用了。

正解邻接矩阵,数据范围小,也很容易想到。

题目中说了,n个点n-1条边的完全图,是个稠密图,用prim

(这次可长记性了,先看啥图,不然跟上面那个一样了就,sad)

 1 #include<bits/stdc++.h>
 2 #define INF 99999
 3 using namespace std;
 4 int t,n;
 5 int a[1005];
 6 int dis[1005];
 7 bool vis[1005];
 8 int mapp[1005][1005];
 9 
10 void prim(int n)
11 {
12     int sum=0;
13     memset(vis, 0, sizeof(vis));
14     for(int i=0; i<n; i++)
15         dis[i]=mapp[0][i];
16     vis[0]=1;
17     int now;
18     for(int i=1; i<n; i++)
19     {
20         int minn=INF;
21         for(int j=0; j<n; j++)
22         {
23             if(!vis[j] && dis[j]<minn)
24             {
25                 minn=dis[j];
26                 now=j;
27             }
28         }
29         sum+=minn;
30         vis[now]=1;
31         for(int j=0; j<n; j++)
32         {
33             if(!vis[j] && dis[j]>mapp[now][j])
34                 dis[j]=mapp[now][j];
35         }
36     }
37     cout<<sum<<endl;
38 }
39 
40 int main()
41 {
42     int dd;
43     cin>>t;
44     while(t--)
45     {
46         cin>>n;
47         for(int i=0; i<n; i++)
48             cin>>a[i];
49         for(int i=0; i<n; i++)
50         {
51             for(int j=i+1; j<n; j++)
52             {
53                 dd=3*(a[i]+a[j])+7;
54                 mapp[i][j]=dd;
55                 mapp[j][i]=dd;
56             }
57         }
58         prim(n);
59     }
60     return 0;
61 }

 

G - 大家快来A水题(更于——5.7)

题目链接 https://acm.sdut.edu.cn/onlinejudge3/contests/3989/problems/G

我以为它骗我,说是水题但是个难题,毕竟压轴。但其实和A和D是一样的。。。。。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,m,u,v;
 4 int p[2002];
 5 
 6 void init(int n)
 7 {
 8     for(int i=1; i<=n; i++)
 9         p[i]=i;
10 }
11 
12 int getp(int o)
13 {
14     return p[o]==o?o:getp(p[o]);
15 }
16 
17 void unio(int x,int y)
18 {
19     int x_root=getp(x);
20     int y_root=getp(y);
21     if(x_root!=y_root) p[y_root]=x_root;
22     return;
23 }
24 
25 int main()
26 {
27     while(cin>>n>>m)
28     {
29         init(n);
30         int ans=0;
31         for(int i=1; i<=m; i++)
32         {
33             cin>>u>>v;
34             unio(u,v);
35         }
36         for(int j=1; j<=n; j++)
37         {
38             if(p[j]==j)
39                 ans++;
40         }
41         cout<<ans<<endl;
42     }
43     return 0;
44 }

 

posted @ 2022-05-06 20:22  爱吃虾滑  阅读(57)  评论(0)    收藏  举报