测试输入包含若干测试用例。每个测试用例的第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
#include<iostream> (720)#include<cstdio> using namespace std; const int MaxN = 1000; int father[MaxN]; int height[MaxN]; void Initial(int n){ for(int i=1; i<=n; i++){ father[i] = i; height[i] = 0; } } int Find(int x){ if(x!=father[x]){ father[x] = Find(father[x]); } return father[x]; } void Union(int x,int y){ x = Find(x); y = Find(y); if(x!=y){ if(height[x]>height[y]){ father[y] = x; } else if(height[x]<height[y]){ father[x] = y; } else{ father[y] = x; height[x]++; } } return; } int main(){ int N,M; while(cin>>N){ if(N==0){ break; } cin>>M; Initial(N); for(int i=0; i<M; i++){ int a,b; cin>>a>>b; Union(a,b); } int answer = -1; for(int i=1; i<=N; i++){ if(i==Find(i)){ answer++; } } cout<<answer<<endl; } return 0; }
//并查集 //求连通分量数 #include<iostream> using namespace std; const int MAX_N = 1001; int father[MAX_N]; int height[MAX_N]; void Init(int n){ // 各结点的父结点初始为自己 // 求连通分量数,即求父结点是自己的结点个数 for(int i = 0; i <= n; i++){ father[i] = i; height[i] = 0; } } // 查,查找父结点 int Find(int x){ // 路径压缩,直接链接到根结点下,使树尽可能矮 if(x != father[x]){ father[x] = Find(father[x]); } return father[x]; } // 并 void Union(int a, int b){ // 查找两个结点的根节点 a = Find(a); b = Find(b); // 根节点不同,进行合并 if(a != b){ // 合并时,把矮的合并到高的,树高不变 if(height[a] < height[b]){ father[a] = b; } else if(height[a] > height[b]){ father[b] = a; } else{ father[a] = b; height[b]++; } } } int main(){ // n 表示城镇数, m 表示道路数 int n, m; while(cin >> n){ if(n == 0) break; cin >> m; // 初始化 Init(n); // 输入道路 while(m--){ int x, y; cin >> x >> y; Union(x, y); } // 经过合并后,连通分量数=父结点是自己的结点数 // 要修建的道路数 = 连通分量数 - 1 // ans 初始为 -1,不是 0 int ans = -1; for(int i = 1; i <= n; i++){ if(father[i] == i){ ans++; } } cout << ans << endl; } return 0; }
#include<iostream> using namespace std; const int maxn = 1000; int father[maxn]; int height[maxn]; void Initial(int n) { for (int i = 0; i <= n; i++) { father[i] = i; height[i] = 0; } } int Find(int x) { if (x != father[x])//当元素不是集合根部时 father[x] = Find(father[x]);//路劲压缩,直接指向根节点,使树高最小 return father[x]; } void Union(int x, int y) { x = Find(x);//先找到集合根部 y = Find(y); if (x != y)//不是同一个集合根部,即不是同一个集合 {//矮树往高树合并,提高查询效率 if (height[x] < height[y]) father[x] = y; else if (height[x] > height[y]) father[y] = x; else//一样高 { father[y] = x; height[x]++; } } } int main() { int N, M; while (cin>>N && N != 0 && cin>>M) { Initial(N); while (M--) { int x, y; cin >> x >> y; Union(x, y); } int ans = -1;//N个城镇最少N-1条路就能连通了 for (int i = 1; i <= N; i++) if (Find(i) == i)//检查集合数量,即没连通的城镇数量 ans++; cout << ans << endl; } return 0; }
#include<iostream> (720)#include<bits/stdc++.h> #define max 1000 using namespace std; int father[max]; int length[max]; int find(int x){ //找节点x的根节点(如果是单个节点,就返回自己) while(father[x]!=-1){ x=father[x]; } return x; } //思想是节点x和y联通,只需要将他们的根节点相连,也相当于他们连通 void union_node(int x,int y){//此函数的目的是将x和y所在的树进行合并,并尽量使高度最小,减少find函数的查找次数 x = find(x); y = find(y); if(x!=y){ //如果x和y节点的根节点相同,说明他们在同一条链中,不操作。 if(length[x]==length[y]){ //高度如果相等,选取任意一根节点作为另一节点的父节点 father[x]=y; length[y]++; //被选取的根节点高度加1 }else{ length[x]>length[y]?father[y]=x:father[x]=y;//不相等,选取高度大的作为父节点 } } } int main(){ int N,M,x,y; while(cin>>N&&N!=0){ cin>>M; memset(father,-1,sizeof(father)); memset(length,0,sizeof(length)); while(M--){ cin>>x>>y; union_node(x,y); } int ans=-1; //父节点为1的节点有树的根节点和不连通的单个节点,所以设为-1去除根节点 for(int i=1;i<=N;i++){ if(father[i]==-1) ans++; } cout<<ans<<endl; } return 0; }
import java.util.Scanner; import java.util.TreeSet; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); while (scanner.hasNext()){ int townNum = scanner.nextInt(); int pathNum = scanner.nextInt(); UnionFind unionFind = new UnionFind(townNum+1); for (int i = 0; i < pathNum; i++) { int town1 = scanner.nextInt(); int town2 = scanner.nextInt(); unionFind.union(town1,town2); } TreeSet<Integer> set = new TreeSet<>(); for (int i = 1; i <=townNum; i++) { set.add(unionFind.find(i)); } System.out.println(set.size()-1); } } public static class UnionFind { private int[] id; public UnionFind(int N) { id = new int[N]; for(int i = 0; i < N; i++) id[i] = i; } public int find(int p) { return id[p]; } public void union(int p, int q){ int pRoot = find(p); int qRoot = find(q); if(pRoot == qRoot) return; for(int i = 0; i < id.length; i++) if(id[i] == pRoot) id[i] = qRoot; } } }
#include <iostream> #include <cstring> using namespace std; #include <algorithm> #define N 1001 int finds(int p[],int x){ while(p[x]>0){ x = p[x]; } return x; } int main(){ int n,m; ///n个城 m条路 int p[N]; int i,a,b; while(cin >> n >> m){ if(n==0) break; memset(p,0,sizeof(p)); for(i=0;i<m;++i){ cin >> a >> b; int x = finds(p,a); int y = finds(p,b); if(x!=y){ p[x] = y; } } int ans = -1; for(i=1;i<=n;i++){ if(p[i]==0){ ++ans;///找多少个非连通分量 } } cout << ans << endl; } return 0; }
#include<iostream> #include<vector> #include<utility> #include<deque> #include<map> using namespace std; vector<pair<int,int>> roads; map<int,bool> m;//<城市,visited> void bfs(int i){ deque<int> d; d.push_back(i); m[i]=true; while(!d.empty()){ int j = d.front(); d.pop_front(); for(pair<int,int> road:roads){ if(road.first==j) { if(!m[road.second]){ m[road.second] = true; d.push_back(road.second); } }else{ if(!m[road.first]){ m[road.first] = true; d.push_back(road.first); } } } } } int main(){ int n; while(cin>>n&&n!=0){ int mm; cin>>mm; for(int i=1;i<=n;i++){ m[i] = false; } for(int i=0;i<mm;i++){ int a,b; cin>>a>>b; pair<int,int> road = make_pair(a,b); roads.push_back(road); } int cons=0; for(int i=1;i<=n;i++){ if(!m[i]){ bfs(i); cons++; } } cout<<cons-1<<endl; } return 0; }
while True: try: def findRoot(b): #找树根 global roads if roads[b] == 0: #树根为0表示自己为根 return b else: temp = findRoot(roads[b]) roads[b] = temp return temp town,roadNum = list(map(int,input().split())) roads = [0 for i in range(town+1)] #一开始每个树根为0,0下标我没用 for i in range(roadNum): tempInput = list(map(int, input().split())) a = findRoot(tempInput[0]) #找出两个新来边的结点的根是否相同 b = findRoot(tempInput[1]) if a != b: #如果根不一样,这条路把他们连接到一起,两棵树缩成一棵树 roads[a] = b print(roads.count(0)-2) #0的数量减去1(下标为0的值为0)得到树的棵数,再减一得到需要造的路 except Exception: break
import java.util.Scanner; public class Main{ static int[] parent; public static void main(String[] args){ Scanner in = new Scanner(System.in); while(in.hasNext()){ int N = in.nextInt(); int M = in.nextInt(); if(N==0) break; parent = new int[N+1]; for(int i=0;i<=N;i++) parent[i]=-1; for(int i=0;i<M;i++){ int left = in.nextInt(); int right = in.nextInt(); int leftParent = findroot(left); int rightParent = findroot(right); if(leftParent!=rightParent) parent[rightParent] = leftParent; } int ans = 0; for(int i=1;i<=N;i++){ if(parent[i]==-1) ans++; } System.out.println(ans-1); } } public static int findroot(int index){ if(parent[index]==-1) return index; int tmp = findroot(parent[index]); parent[index] = tmp; return tmp; } }
#include <iostream> #include <vector> #include <queue> using namespace std; int find(const vector<bool> & v) { for(int i = 0; i<v.size(); i++) { if(v[i] == false) { return i; } } return -1; } int main() { //freopen("t.txt", "r", stdin); int N; while(cin>>N, N) { int M; cin>>M; vector<vector<bool>> mat(N, vector<bool>(N)); for(int i = 0; i<M; i++) { int a, b; cin>>a>>b; a--,b--; mat[a][b] = true; mat[b][a] = true; } vector<bool> traversed(N, false); int start = 0; int res = -1; do { res++; queue<int> q; q.push(start); traversed[start] = true; while(!q.empty()) { int f = q.front(); q.pop(); for(int link = 0; link < N; link++) { if(f == link) continue; if(mat[f][link] && traversed[link]==false) { q.push(link); traversed[link] = true; } } } } while((start=find(traversed)) != -1); cout<<res<<endl; } }
#include<iostream> using namespace std; int FindRoot(int a,int Tree[]){ if(Tree[a]==-1) return a; else{ int tmp=FindRoot(Tree[a],Tree); Tree[a]=tmp; return tmp; } } int main(){ int n,m; int Tree[1001]; while(cin>>n>>m&&n!=0){ int a,b; for(int i=1;i<=n;i++) Tree[i]=-1; for(int i=0;i<m;i++){ cin>>a>>b; a=FindRoot(a,Tree); b=FindRoot(b,Tree); if(a!=b) Tree[b]=a; } int ans=0; for(int i=1;i<=n;i++) if(Tree[i]==-1) ans++; cout<<ans-1<<endl; } return 0; }
#include <stdio.h> #define N 1000 int parent[N];//parent[i]表示节点i的双亲是谁, -1表示不存在 int FindRoot(int x)//寻找x所在的树的根节点 { if(parent[x]==-1) return x; else//递归调用 { int ret=FindRoot(parent[x]); parent[x]=ret;//路径压缩 return ret; } } int main() { int m, n; while(scanf("%d", &n)!=EOF) { if(n<=0) break; scanf("%d", &m); for(int i=1; i<=n; i++) parent[i]=-1;//一开始全是根 while(m--) { int x, y; scanf("%d %d", &x, &y); x=FindRoot(x); y=FindRoot(y); if(x!=y) parent[y]=x;//不在同一棵树那就合并 } int count=0;//统计一下根节点数量 for(int i=1; i<=n; i++) { if(parent[i]==-1) count++; } printf("%d\n", count-1); } return 0; }
//老哥们一看就是用的并查集 //我换种解法:dfs #include<iostream> using namespace std; const int maxn=1001,INF=1000000000; int n,m,g[maxn][maxn],d[maxn],vis[maxn]; void dfs(int s) { vis[s]=1; for(int i=1;i<=n;i++) { if(vis[i]==0&&g[s][i]!=INF) dfs(i); } } int main() { int a,b,ans; while(cin>>n&&n) { cin>>m; ans=0; fill(vis,vis+maxn,0); fill(g[0],g[0]+maxn*maxn,INF); for(int i=0;i<m;i++) { cin>>a>>b; g[a][b]=g[b][a]=1; } for(int i=1;i<=n;i++) { if(vis[i]==0) { ans++; dfs(i); } } cout<<ans-1<<endl; } return 0; }
#include<stdio.h> int father[1000]; void Init(int a[],int n){ for(int i=0;i<n;i++){ a[i] = i; } } int FindFather(int n){ if(father[n]==n) return n; else { father[n] = FindFather(father[n]); return father[n]; } } void Union(int a,int b){ int root1 = FindFather(a); int root2 = FindFather(b); father[root1] = root2; } int main(){ int N;int M; while(scanf("%d%d",&N,&M)!=EOF){ if(N==0) return 0; Init(father,1000); int count = 0; for(int i=0;i<M;i++){ int a,b; scanf("%d%d",&a,&b); Union(a,b); } for(int i=1;i<N;i++){ int r1 = FindFather(i); int r2 = FindFather(i+1); if(r1!=r2) {Union(r1, r2); count++;} } printf("%d\n",count); } }
#include <iostream> #include <type_traits> #include <vector> using namespace std; const int MAXN = 1000; vector<int>father(MAXN); //父亲结点 vector<int>height(MAXN); //结点高度 void Initial(int n) { //初始化 for (int i = 0; i <= n; i++) { father[i] = i; //每个结点的父亲为自己 height[i] = 0; //每个结点的高度为0 } } int Find(int x) { //查找根结点 if (x != father[x]) { //路径压缩 father[x] = Find(father[x]); } return father[x]; } void Union(int x, int y) { //合并集合 x = Find(x); y = Find(y); if (x != y) { //矮树作为高树的子树 if (height[x] < height[y]) { father[x] = y; } else if (height[y] < height[x]) { father[y] = x; } else { father[y] = x; height[x]++; } } } int main() { int n, m; while (cin >> n && n != 0) { cin >> m; Initial(n); //初始化 while (m--) { int x, y; cin >> x >> y; Union(x, y); //合并集合 } int answer = -1; for (int i = 1; i <= n; i++) { if (Find(i) == i) { //集合数目 answer++; } } cout << answer << endl; } return 0; }
没怎么写过并查集,很粗糙 #include "iostream" #include <vector> #include <algorithm> using namespace std; vector<int> parent; int find(int a){ if(parent[a]== -2){ parent[a] = -1; // 加入后初始化 } if(parent[a]!=-1){ a = find(parent[a]); } return a; } void to_union(int a,int b){ int x = find(a); int y = find(b); if(x != y){ parent[y] = x; } } int main(){ int size,num; while(cin >> size>>num){ if(num==0) { cout << size - 1 << endl; continue; } parent.resize(size,-2); for(int i=0; i < num;++i){ int a,b; cin >> a >> b; to_union(a-1,b-1); } // 然后找parent中不同的个数 sort(parent.begin(),parent.end()); int count_set = 0; int count_num = 0; for (int i = 0; i < size; ++i) { // cout << parent[i] << " "; if(parent[i]==-1) {count_num++; count_set++; } if(parent[i]>=0) count_num++; } // cout << endl; cout << size-count_num+count_set-1 << endl; } }
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int N; while (scanner.hasNext() && (N = scanner.nextInt()) != 0) { int M = scanner.nextInt(); UnionFindSet ufs = new UnionFindSet(N, 1000); for (int i = 0; i < M; i++) { ufs.union(scanner.nextInt(), scanner.nextInt()); } int count = -1; for (int i = 1; i <= N; i++) { if (ufs.father[i] == i) { count++; } } System.out.println(count); } } public static class UnionFindSet { private int father[]; private int height[]; public UnionFindSet(int n, int max) { this.father = new int[max]; this.height = new int[max]; for (int i = 1; i <= n; i++) { this.father[i] = i; this.height[i] = 0; } } int find(int x) { if (x != father[x]) { father[x] = find(father[x]); } return father[x]; } void union(int x, int y) { x = find(x); y = find(y); if (x != y) { if (height[x] < height[y]) { father[x] = y; } else if (height[x] > height[y]) { father[y] = x; } else { father[y] = x; height[x]++; } } } } }