# DFS 暴力做法 from sys import setrecursionlimit setrecursionlimit(1000000) defdfs(cur): # traversal from the city whose index is cur global n,g,visited visited[cur] = True # search next city for i in g[cur]: if visited[i]: continue dfs(i)
defcounter(): # traversal all if the city,everytime it uses the dfs() function,add one to the answer global n,visited ans = 0 for i inrange(1,n+1): if visited[i]: continue # hasn't been visited yet ans += 1 # mark all of the cities that belong to the same group dfs(i) return ans
# main part n,m = map(int,input().split()) # create a list to record the roads g = [[] for i inrange(n+1)] for _ inrange(m): u,v = map(int,input().split()) g[u].append(v) g[v].append(u) # create a visited list to record whether the city has been visited or not visited = [Falsefor i inrange(n+1)] visited[0] = True print(counter())
# 并查集模板题 # 找出x所在树的根节点 deffind(x): if pre[x] != x: return find(pre[x]) return x # 判断两个节点是否在同一个树(城市群)上,如果不在,则合并两个城市群并计数 defjoin(x,y): global n x_root = find(x) # 找出x所在树的根节点 y_root = find(y) # 找出y所在树的根节点 if x_root != y_root: # 两个节点不在同一个树上(城市群),将两个城市群合并,以后再碰到这两个树的节点就不会重复计数了,保证每一颗树只计数一次 pre[x_root] = y_root # 将x_root变成y_root的子节点,合并两树 # 初始时有n个节点,彼此没有路径关系,视为n个城市群 # 随着道路关系的引入,城市群不断合并,n就是城市群的数量 n -= 1
# 主程序 n,m = map(int, input().split()) # 注意序号从1开始 pre = [i for i inrange(n+1)] for _ inrange(m): u,v = map(int,input().split()) join(u,v) print(n)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
# 优化后的并查集,在找到x的根节点后直接将x的前驱节点改为根节点,缩短x的子节点查找根节点的路径长度 n, m = map(int, input().split()) p = list(range(n + 1)) deffind_root(x): if p[x] == x: return x p[x] = find_root(p[x]) return pre[x] for i inrange(m): u, v = map(int, input().split()) u_root = find_root(u) v_root = find_root(v) if u_root != v_root: p[u_root] = v_root n-=1 print(n)
例题 2:修改数组(第10届蓝桥杯省赛真题)
题目描述:
给定一个长度为 N 的数组 A=[A1,A2,⋅⋅⋅,AN],数组中有可能有重复出现的整数。
现在小明要按以下方法将其修改为没有重复整数的数组。小明会依次修改 A2,A3,⋅⋅⋅,AN。
当修改 Ai 时,小明会检查 Ai 是否在 A1∼Ai−1 中出现过。如果出现过,则小明会给 Ai 加上 1 ;如果新的 Ai 仍在之前出现过,小明会持续给 Ai 加 1 ,直到 Ai 没有在 A1∼Ai−1 中出现过。
当 AN 也经过上述修改之后,显然 A 数组中就没有重复的整数了。现在给定初始的 A 数组,请你计算出最终的 A 数组。
# 并查集 deffind(x): if x == f[x]: # 找到还没有出现过的元素 return x p = x while p != f[p]: p = f[p] f[x] = p return p n = int(input()) a = [int(i) for i ininput().split()] f = [i for i inrange(1000001)] # 使用a[]中元素的最大值作为并查集数组容量 for i inrange(n): # 更新a[] a[i] = find(a[i]) # 更新并查集 f[a[i]] = find(a[i]+1) print(' '.join(list(map(str, a))))
deffind(x): if x == fa[x]: return x p = x while p != fa[p]: p = fa[p] # 合并路径版的并查集 fa[x] = p return p
deftarjan(x): visited[x] = True # 遍历所有子节点 for i in e[x]: if visited[i]: continue tarjan(i) fa[i] = x # 检查以x为主元素的查询 for t in query[x]: if visited[t[0]]: ans[t[1]] = find(t[0])
n,m,s = map(int,input().split()) e = [[] for _ inrange(n+1)] # 存储边的关系 visited = [False]*(n+1) # 并查集 fa = [i for i inrange(n+1)] # 存储查询,元素类型是二元组 query = [[] for _ inrange(n+1)] # 存储结果 ans = [-1]*m # 接收输入,构造树 for _ inrange(n-1): x,y = map(int,input().split()) e[x].append(y) e[y].append(x) # 接收查询信息 for i inrange(m): x,y = map(int,input().split()) # 在记录每一组查询的同时记录查询的次序 query[x].append((y,i)) query[y].append((x,i))
tarjan(s) # 输出结果 for i in ans: if i == -1: continue print(i)