测试输入包含若干测试用例。每个测试用例的第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 <iostream> #include <vector> using namespace std; void unionset(vector<int> &build,int u,int v){//连接uv两个集合 while(build[u]!=u){u=build[u];} while(build[v]!=v){v=build[v];} build[u]=v; } int findset(vector<int> &build,int u){//找到u属于哪个集合 if(build[u]!=u){build[u]=findset(build,build[u]);} return build[u]; } int main() { int town,road; while(cin >> town >> road){ vector<int> build(town + 1); for(int i=0;i<=town;i++){ build[i]=i; } int u,v; for(int i=0;i<road;i++){ cin >> u >> v; unionset(build,u,v); } int alreadyconnedted[town+1]; for(int i=0;i<=town;i++){ alreadyconnedted[i]=0; } int count=0; for(int i=1;i<=town;i++){ int x=findset(build,i); if(alreadyconnedted[x]==0){count++;} alreadyconnedted[x]=1; } cout << count-1 << endl; } }
#include <stdio.h> #include <string.h> int father[1000]; int height[1000]; // Add proper parameter types in function declarations void intial(int n) { for(int i = 0; i < n; i++) { father[i] = i; height[i] = 0; } } int Find(int n) { if(n != father[n]) { father[n] = Find(father[n]); } return father[n]; } void Uion(int a, int b) { a = Find(a); b = Find(b); if (a != b) { if (height[a] > height[b]) { father[b] = a; // Fix assignment } else if (height[a] < height[b]) { father[a] = b; // Fix assignment } else { father[a] = b; height[b]++; } } } int main() { int a, b, c, d; while(scanf("%d %d", &a, &b) != EOF) { if (a == 0){ break; } int ans = -1; intial(a); for(int i = 0; i < b; i++) { scanf("%d %d", &c, &d); Uion(c, d); } for(int i = 0; i < a; i++) { // Change b to a if(i == father[i]) ans++; } printf("%d\n", ans); // Add output statement } return 0; }
#include <iostream> using namespace std; int village[1010]; void Initset(int n){ for(int i=0;i<n;i++){ village[i]=i; } } int findfather(int n) { if (village[n] == n) { return n; } else return findfather(village[n]); } int main() { int n,m; int start,end; while (cin>>n>>m) { // 注意 while 处理多个 case if(n==0){ break; } Initset(n+1); int setnode=n; for(int i=0;i<m;i++){ cin>>start>>end; int startroot=findfather(start); int endroot=findfather(end); if(startroot!=endroot){ setnode--; village[endroot]=startroot; } } cout<<setnode-1<<endl; } return 0; }
#include <bits/stdc++.h> using namespace std; int fa[1005]; int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); } void merge(int x, int y) { x = find(x); y = find(y); if(x<y) swap(x,y); fa[x] = y; } int main() { int n, m; while (cin >> n >> m) { for (int i = 1; i <= n; i++) fa[i] = i; while (m--) { int a, b; cin >> a >> b; merge(a, b); } int cnt = -1; for(int i=1;i<=n;i++) if(find(i)==i) cnt++; cout<<cnt; } 输出并查集数-1