并查集最常用来判断图是否为连通图,或用来求图的连通分量数。
并查集1--<=>求连通分量个数
题目描述
某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?
输入描述:
测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。
注意:两个城市之间可以有多条道路相通,也就是说
3 3
1 2
1 2
2 1
这种输入也是合法的
当N为0时,输入结束,该用例不被处理。
输出描述:
对每个测试用例,在1行里输出最少还需要建设的道路数目。
输入
4 2
1 3
4 3
3 3
1 2
1 3
2 3
5 2
1 2
3 5
999 0
0
输出
1
0
2
998
1 #include<bits/stdc++.h>
2 using namespace std;
3 int father[1005];
4 int height[1005];
5 void Initial(int n){//初始化
6 for(int i=0;i<n;i++){
7 father[i]=i;
8 height[i]=0;
9 }
10 }
11 int Find(int x){//路径压缩
12 if(x!=father[x]) father[x]=Find(father[x]);
13 return father[x];
14 }
15 void Union(int x,int y){//合并----实际上为了使得查找效率更高 可以让高度较低的树 作为高度较高的树的子树来合并。
16 x=Find(x);
17 y=Find(y);
18 if(x!=y) father[x]=y;
19 }
20 int main(){
21 int n,m;
22 while(cin>>n>>m){
23 Initial(n);
24 for(int i=0;i<m;i++){
25 int x,y;
26 cin>>x>>y;
27 Union(x,y);
28 }
29 int ans=0;
30 for(int i=0;i<m;i++){
31 if(Find(i)==i) ans++;//如果根就是自己,代表了一个连通分量。
32 }
33 cout<<ans-1<<endl;
34 }
35 return 0;
36 }
并查集2---<=>判断图是否连通
题目描述
给定一个无向图和其中的所有边,判断这个图是否所有顶点都是连通的。
输入描述:
每组数据的第一行是两个整数 n 和 m(0<=n<=1000)。n 表示图的顶点数目,m 表示图中边的数目。随后有 m 行数据,每行有两个值 x 和 y(0<x, y <=n),表示顶点 x 和 y 相连,顶点的编号从 1 开始计算。输入不保证这些边是否重复。
输出描述:
对于每组输入数据,如果所有顶点都是连通的,输出"YES",否则输出"NO"。
示例1
输出
复制NO
YES
思路:计算图的连通分量 个数,如果为1,那说明是图,否则不是图,输出NO。
1 #include<bits/stdc++.h>
2 using namespace std;
3 int father[1000];
4 int height[1000];
5 void Initial(int n){
6 for(int i=0;i<n;i++){
7 father[i]=i;
8 height[i]=0;
9 }
10 }
11 int Find(int x){
12 if(x!=father[x])
13 father[x]=Find(father[x]);
14 return father[x];
15 }
16 void Union(int x,int y){
17 x=Find(x);
18 y=Find(y);
19 if(x!=y)
20 father[x]=y;
21 }
22 int main(){
23 int n,m;
24 while(cin>>n>>m){
25 if(n==0&&m==0) break;
26 Initial(n);
27 while(m--){
28 int x,y;
29 cin>>x>>y;
30 Union(x,y);
31 }
32 int cnt=0;
33 for(int i=0;i<n;i++){
34 if(Find(i)==i) cnt++;
35 }
36 if(cnt==1) cout<<"YES"<<endl;
37 else cout<<"NO"<<endl;
38 }
39 return 0;
40 }
并查集3---<=>判断是否是一棵树
判断是否为树的方法:1、这个图是连通的,即连通分量个数=1, 2、根节点入度=0,其他节点入度=1.
题目描述
A tree is a well-known data structure that is either empty (null, void, nothing) or is a set of one or more nodes connected by directed edges between nodes satisfying the following properties. There is exactly one node, called the root, to which no directed edges point. Every node except the root has exactly one edge pointing to it. There is a unique sequence of directed edges from the root to each node. For example, consider the illustrations below, in which nodes are represented by circles and edges are represented by lines with arrowheads. The first two of these are trees, but the last is not.
In this problem you will be given several descriptions of collections of nodes connected by directed edges. For each of these you are to determine if the collection satisfies the definition of a tree or not.
输入描述:
The input will consist of a sequence of descriptions (test cases) followed by a pair of negative integers. Each test case will consist of a sequence of edge descriptions followed by a pair of zeroes Each edge description will consist of a pair of integers; the first integer identifies the node from which the edge begins, and the second integer identifies the node to which the edge is directed. Node numbers will always be greater than zero and less than 10000.
输出描述:
For each test case display the line "Case k is a tree." or the line "Case k is not a tree.", where k corresponds to the test case number (they are sequentially numbered starting with 1).
示例1
输入
6 8 5 3 5 2 6 4
5 6 0 0
8 1 7 3 6 2 8 9 7 5
7 4 7 8 7 6 0 0
3 8 6 8 6 4
5 3 5 6 5 2 0 0
-1 -1
输出
Case 1 is a tree.
Case 2 is a tree.
Case 3 is not a tree.
1 #include<bits/stdc++.h>
2 using namespace std;
3 int father[1000];
4 int height[1000];
5 int inD[1000];//入度数组
6 bool visit[1000];
7 void Initial(){
8 for(int i=0;i<1000;i++){
9 father[i]=i;
10 height[i]=0;
11 inD[i]=0;
12 visit[i]=false;
13 }
14 }
15 int Find(int x){
16 if(x!=father[x]) father[x]=Find(father[x]);
17 return father[x];
18 }
19 void Union(int x,int y){
20 x=Find(x);
21 y=Find(y);
22 if(x!=y) father[x]=y;
23 }
24 bool isTree(){
25 bool flag=true;
26 int cnt=0,root=0;
27 for(int i=0;i<1000;i++){
28 if(visit[i]==false) continue;
29 if(Find(i)==i) cnt++;
30 if(inD[i]==0) root++;
31 if(inD[i]>1) flag=false;
32 }
33 if((cnt!=1)||root!=1) flag=false;//不连通、根的入度!=0、其他节点入度>1 都不是树
34 if(cnt==0&&root==0) flag=true;//空树也是树!
35 return flag;
36 }
37 int main(){
38 int x,y;
39 int num=1;
40 Initial();//初始化
41 while(cin>>x>>y){
42 if(x==-1&&y==-1) break;
43 if(x==0&&y==0){
44 if(isTree()) cout<<"Case "<<num++<<" is a tree."<<endl;
45 else cout<<"Case "<<num++<<" is not a tree."<<endl;
46 Initial();//每次结束之后都要初始化
47 }else{
48 Union(x,y);
49 inD[y]++;//入度增加
50 visit[x]=true,visit[y]=true;//标记以访问
51 }
52 }
53 return 0;
54 }
最小生成树1--kruskal算法(每次选择权值最小的,如果没有被。。那就加入~)
题目描述
省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可)。现得到城镇道路统计表,表中列出了任意两城镇间修建道路的费用,以及该道路是否已经修通的状态。现请你编写程序,计算出全省畅通需要的最低成本。
输入描述:
测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( 1< N < 100 );随后的 N(N-1)/2 行对应村庄间道路的成本及修建状态,每行给4个正整数,分别是两个村庄的编号(从1编号到N),此两村庄间道路的成本,以及修建状态:1表示已建,0表示未建。
当N为0时输入结束。
输出描述:
每个测试用例的输出占一行,输出全省畅通需要的最低成本。
示例1
输出
复制3
1
0
1 #include<bits/stdc++.h>
2 using namespace std;
3 struct edge{
4 int from;
5 int to;
6 int cost;
7 int status;
8 };
9 edge a[10000];
10 int father[100];
11 int height[100];
12 bool cmp(edge x,edge y){//kruskal算法按边权值从小到大排序
13 return x.cost<y.cost;
14 };
15 void Initial(int n){
16 for(int i=0;i<n;i++){
17 father[i]=i;
18 height[i]=0;
19 }
20 }
21 int Find(int x){
22 if(x!=father[x]) father[x]=Find(father[x]);
23 return father[x];
24 }
25 void Union(int x,int y){
26 if(Find(x)!=Find(y)){
27 father[Find(x)]=Find(y);
28 }
29 }
30 int main(){
31 int n;
32 while(cin>>n&&n!=0){
33 Initial(n);//初始化
34 int ans=0,num=(n-1)*n/2;
35 for(int i=0;i<num;i++){
36 cin>>a[i].from>>a[i].to>>a[i].cost>>a[i].status;
37 if(a[i].status==1) a[i].cost=0;//表示已经有的道路。
38 }
39 sort(a,a+num,cmp);//按权值排序
40 for(int i=0;i<num;i++){//kruskal 算法
41 if(Find(a[i].from)!=Find(a[i].to)){//如果没有 就加入
42 Union(a[i].from,a[i].to);
43 ans+=a[i].cost;
44 }
45 }
46 cout<<ans<<endl;
47 }
48 return 0;
49 }
最小生成树2---kruskal算法
题目描述
In an episode of the Dick Van Dyke show, little Richie connects the freckles on his Dad's back to form a picture of the Liberty Bell. Alas, one of the freckles turns out to be a scar, so his Ripley's engagement falls through. Consider Dick's back to be a plane with freckles at various (x,y) locations. Your job is to tell Richie how to connect the dots so as to minimize the amount of ink used. Richie connects the dots by drawing straight lines between pairs, possibly lifting the pen between lines. When Richie is done there must be a sequence of connected lines from any freckle to any other freckle.
输入描述:
The first line contains 0 < n <= 100, the number of freckles on Dick's back. For each freckle, a line follows; each following line contains two real numbers indicating the (x,y) coordinates of the freckle.
输出描述:
Your program prints a single real number to two decimal places: the minimum total length of ink lines that can connect all the freckles.
示例1
输出
复制3.41
本题与上题最大的不同在于要先将坐标转换成边的信息。
1 #include<bits/stdc++.h>
2 using namespace std;
3 struct Edge{
4 int from;
5 int to;
6 float len;
7 };
8 struct Point{
9 float x;
10 float y;
11 };
12 int father[105];
13 int height[100];
14 Edge edge[10000];
15 Point point[105];
16 void Initial(int n){
17 for(int i=0;i<n;i++){
18 father[i]=i;
19 height[i]=0;
20 }
21 }
22 int Find(int x){
23 if(x!=father[x]) father[x]=Find(father[x]);
24 return father[x];
25 }
26 void Union(int x,int y){
27 x=Find(x);
28 y=Find(y);
29 if(x!=y){
30 father[x]=y;
31 }
32 }
33 bool cmp(Edge x,Edge y){
34 return x.len<y.len;
35 }
36 int main(){
37 int n;
38 while(cin>>n){
39 Initial(n);
40 // int num=(n-1)-n/2;
41 for(int i=0;i<n;i++){
42 cin>>point[i].x>>point[i].y;
43 }
44 int num=0;
45 for(int i=0;i<n;i++){
46 for(int j=i+1;j<n;j++){
47 int s1=pow(point[i].x-point[j].x,2);
48 int s2=pow(point[i].y-point[j].y,2);
49 float ss=sqrt(s1+s2);
50 edge[num].from=i;
51 edge[num].to=j;
52 edge[num].len=ss;
53 num++;//边的个数
54 }
55 }//初始化 边:
56 sort(edge,edge+num,cmp);//按边的长度大小排序。
57 float sum=0;
58 for(int i=0;i<num;i++){
59 if(Find(edge[i].from)!=Find(edge[i].to)){
60 Union(edge[i].from,edge[i].to);
61 sum+=edge[i].len;
62 }
63 }
64 cout<<fixed<<setprecision(2)<<sum<<endl;
65 }
66 return 0;
67 }
最小生成树3
题目描述
The Head Elder of the tropical island of Lagrishan has a problem. A burst of foreign aid money was spent on extra roads between villages some years ago. But the jungle overtakes roads relentlessly, so the large road network is too expensive to maintain. The Council of Elders must choose to stop maintaining some roads. The map above on the left shows all the roads in use now and the cost in aacms per month to maintain them. Of course there needs to be some way to get between all the villages on maintained roads, even if the route is not as short as before. The Chief Elder would like to tell the Council of Elders what would be the smallest amount they could spend in aacms per month to maintain roads that would connect all the villages. The villages are labeled A through I in the maps above. The map on the right shows the roads that could be maintained most cheaply, for 216 aacms per month. Your task is to write a program that will solve such problems.(ps:如果不是连通图的话,最后的结果输出不用包含不在连通图里的那些点)
输入描述:
The input consists of one to 100 data sets, followed by a final line containing only 0. Each data set starts with a line containing only a number n, which is the number of villages, 1 < n < 27, and the villages are labeled with the first n letters of the alphabet, capitalized. Each data set is completed with n-1 lines that start with village labels in alphabetical order. There is no line for the last village. Each line for a village starts with the village label followed by a number, k, of roads from this village to villages with labels later in the alphabet. If k is greater than 0, the line continues with data for each of the k roads. The data for each road is the village label for the other end of the road followed by the monthly maintenance cost in aacms for the road. Maintenance costs will be positive integers less than 100. All data fields in the row are separated by single blanks. The road network will always allow travel between all the villages. The network will never have more than 75 roads. No village will have more than 15 roads going to other villages (before or after in the alphabet). In the sample input below, the first data set goes with the map above.
输出描述:
The output is one integer per line for each data set: the minimum cost in aacms per month to maintain a road system that connect all the villages. Caution: A brute force solution that examines every possible set of roads will not finish within the one minute time limit.
示例1
输入
复制9
A 2 B 12 I 25
B 3 C 10 H 40 I 8
C 2 D 18 G 55
D 1 E 44
E 2 F 60 G 38
F 0
G 1 H 35
H 1 I 35
3
A 2 B 10 C 40
B 1 C 20
0
输出
复制216
30
本题需要将输入中的 信息转化
1 #include<bits/stdc++.h>
2 using namespace std;
3 struct edge{
4 int from;
5 int to;
6 int len;
7 };
8 edge a[1000];
9 int father[30];
10 int height[30];
11 void Initial(int n){
12 for(int i=0;i<n;i++){
13 father[i]=i;
14 height[i]=0;
15 }
16 }
17 int Find(int x){
18 if(x!=father[x]) father[x]=Find(father[x]);
19 return father[x];
20 }
21 void Union(int x,int y){
22 x=Find(x);
23 y=Find(y);
24 if(x!=y) father[x]=y;
25 }
26 bool cmp(edge x, edge y){
27 return x.len<y.len;
28 }
29 int main(){
30 int n;
31 while(cin>>n&&n!=0){
32 Initial(n);
33 int num=0,cnt=0;
34 for(int i=0;i<n-1;i++){
35 char from;
36 int m;
37 cin>>from>>m;
38 num+=m;//num为边的个数---
39 while(m--){
40 char to;
41 int x;
42 cin>>to>>x;
43 a[cnt].from=from-'A';
44 a[cnt].to=to-'A';
45 a[cnt].len=x;
46 cnt++;
47 }
48 }
49 int sum=0;
50 sort(a,a+num,cmp);//边按权值大小排序
51 for(int i=0;i<num;i++){
52 if(Find(a[i].from)!=Find(a[i].to)){//
53 Union(a[i].from,a[i].to);
54 sum+=a[i].len;
55 }
56 }
57 cout<<sum<<endl;
58 }
59 return 0;
60 }
最小生成树总结:依然是并查集的板子,但是需要对所有边的权值按从小到大的顺序排序,如果之前未被合并过,那就合并(kruskal算法),有时需要将坐标、字母等转化成边的信息(起点、终点、权值)。
最短路径问题1
最短路径问题主要两种算法:Dijkstra算法与Floyd算法
1、Dijkstra算法:适合单源最短路径问题,
2、Floyd算法:多源最短路径。思路简单。但是机试要求顶点个数<100/200,否则会超时。
Dijkstra算法1
题目描述
给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的。
输入描述:
输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示a和b之间有一条边,且其长度为d,花费为p。最后一行是两个数 s,t;起点s,终点t。n和m为0时输入结束。
(1<n<=1000, 0<m<100000, s != t)
输出描述:
输出 一行有两个数, 最短距离及其花费。
示例1
输出
复制9 11
1 #include<bits/stdc++.h>
2 using namespace std;
3 struct Edge{
4 int to;
5 int len;
6 int cost;
7 Edge(int t,int l,int c):to(t),len(l),cost(c){}
8 };
9 struct Point{
10 int num;
11 int distance;
12 Point(int n,int d):num(n),distance(d){}
13 bool operator<(const Point &c)const{
14 return distance>c.distance;//与sort反着的。因为优先队列 int型默认数越大优先级越高。
15 }
16 };
17 vector<Edge>graph[100000];//邻接表建立图
18 int dis[1005];//存储最短路径
19 int cost[1005];
20 void dij(int s){
21 priority_queue<Point>my;//按照距离越小优先级越高来建立优先队列。
22 dis[s]=0,cost[s]=0;//初始化
23 my.push(Point(s,dis[s]));//压入初始点
24 //开始遍历整个图
25 while(!my.empty()){
26 int u=my.top().num;//每次选择离源点最近的点
27 my.pop();//****
28 for(int i=0;i<graph[u].size();i++){//直到遍历完邻接表中所有节点。
29 int v=graph[u][i].to;
30 int l=graph[u][i].len;
31 int c=graph[u][i].cost;
32 if(dis[v]>l+dis[u]||(dis[v]==l+dis[u]&&cost[v]>c+cost[u])){//两种情况:1、路径长度可优化 2、路径长度相等时,按照花费小的来。==========针对最短路径的变种 基本来自于松弛条件
33 dis[v]=l+dis[u];
34 cost[v]=c+cost[u];
35 my.push(Point(v,dis[v]));
36 }
37 }
38 }
39 }
40 int main(){
41 int n,m;
42 while(cin>>n>>m){
43 if(n==0&&m==0) break;
44 memset(graph,0,sizeof(graph));//memset在初始化的时候有限制,只能为0 -1 什么的。所以下面初始为无穷时用fill函数
45 fill(dis,dis+n+1,INT_MAX);//初始化
46 fill(cost,cost+n+1,INT_MAX);//勿忘初始化!!!!
47 for(int i=0;i<m;i++){
48 int from,to,len,cost;
49 cin>>from>>to>>len>>cost;
50 graph[from].push_back(Edge(to,len,cost));
51 graph[to].push_back(Edge(from,len,cost));
52 }
53 int s,t;
54 cin>>s>>t;
55 dij(s);
56 cout<<dis[t]<<' '<<cost[t]<<endl;//用dis距离数组、cost花费数组来存储 最终的距离 还是很nice的想法。
57 }
58 return 0;
59 }
Floyd算法1
题目描述
The country is facing a terrible civil war----cities in the country are divided into two parts supporting different leaders. As a merchant, Mr. M does not pay attention to politics but he actually knows the severe situation, and your task is to help him reach home as soon as possible. "For the sake of safety,", said Mr.M, "your route should contain at most 1 road which connects two cities of different camp." Would you please tell Mr. M at least how long will it take to reach his sweet home?
输入描述:
The input contains multiple test cases.
The first line of each case is an integer N (2<=N<=600), representing the number of cities in the country.
The second line contains one integer M (0<=M<=10000), which is the number of roads.
The following M lines are the information of the roads. Each line contains three integers A, B and T, which means the road between city A and city B will cost time T. T is in the range of [1,500].
Next part contains N integers, which are either 1 or 2. The i-th integer shows the supporting leader of city i.
To simplify the problem, we assume that Mr. M starts from city 1 and his target is city 2. City 1 always supports leader 1 while city 2 is at the same side of leader 2.
Note that all roads are bidirectional and there is at most 1 road between two cities.
Input is ended with a case of N=0.
输出描述:
For each test case, output one integer representing the minimum time to reach home.
If it is impossible to reach home according to Mr. M's demands, output -1 instead.
示例1
输入
复制2
1
1 2 100
1 2
3
3
1 2 100
1 3 40
2 3 50
1 2 1
5
5
3 1 200
5 3 150
2 5 160
4 3 170
4 2 170
1 2 2 2 1
0
输出
复制100
90
540
思路:Floyd算法+限制条件求最短路径
1 #include<bits/stdc++.h>//Floyd
2 using namespace std;
3 int G[605][605];//邻接矩阵建图=========牛客网 600的数据能通过,但是机试时可能会超时。
4 int sp[605];
5 int n;
6 void floyd(int n){//Floyd
7 for(int k=1;k<=n;k++){
8 for(int i=1;i<=n;i++){
9 for(int j=1;j<=n;j++){
10 if(G[i][j]>G[i][k]+G[k][j]){
11 G[i][j]=G[i][k]+G[k][j];
12 }
13 }
14 }
15 }
16 return;
17 }
18 int main(){
19 int m;
20 while(cin>>n){
21 if(n==0)
22 break;
23 cin>>m;
24 for(int i=1;i<=n;i++){
25 for(int j=1;j<=n;j++){
26 G[i][j]=10000000;//初始化
27 }
28 G[i][i]=0;
29 }
30 for(int i=0;i<m;i++){
31 int from,to,len;
32 cin>>from>>to>>len;
33 if(G[from][to]>len)
34 {G[from][to]=len;
35 G[to][from]=len;}
36 }
37 for(int i=1;i<=n;i++){
38 cin>>sp[i];
39 }
40 for(int i=1;i<=n;i++){
41 for(int j=1;j<=n;j++){
42 if(sp[i]==2&&sp[j]==1)//这样可限制为有向图,即从支持2的城市不可到达支持1的城市
43 G[i][j]=10000000;
44 }
45 }
46 floyd(n);
47 if(G[1][2]==10000000)
48 cout<<"-1"<<endl;
49 else cout<<G[1][2]<<endl;
50 }
51 return 0;
52 }
最短路径总结:如果题目节点个数较小,那么可以使用较为简单的Floyd算法,否则Dijkstra算法。
来源:oschina
链接:https://my.oschina.net/u/4370441/blog/3235029