1. 问题描述:
有 n 个气球(编号 1∼n),其中第 i 个气球的颜色为 ci。气球一共有 k 种颜色(编号 1∼k),每个气球的颜色 ci 都满足 1 ≤ ci ≤ k。接下来要进行 m 次询问,每次询问给定两个整数 l,r,并询问第 l 个气球和第 r 个气球的颜色是否相同。我们希望所有询问都能得到肯定的回答(即每次询问的两个气球的颜色都相同)。为了达成这一目的,我们可以对其中一些气球进行重新染色,被重新染色的气球的颜色也应在 [1,k] 范围内。为了节约染料,我们希望重新染色的气球数量尽可能少。请问,最少需要重新染色多少个气球。注意,所有染色必须在第一次询问开始之前完成。
输入格式
第一行包含三个整数 n,m,k。第二行包含 n 个整数 c1,c2,…,cn。接下来 m 行,每行包含两个整数 l,r。
输出格式
一个整数,表示最少需要重新染色的气球数量。
数据范围
前 5 个测试点满足,2 ≤ n ≤ 10,0 ≤ m ≤ 5,1 ≤ k ≤ 5。
所有测试点满足,2 ≤ n ≤ 2 × 10 ^ 5,0 ≤ m ≤ 2 × 10 ^ 5,1 ≤ k ≤ 2 × 10 ^ 5,1 ≤ ci ≤ k,1 ≤ l,r ≤ n,l ≠ r。
输入样例1:
3 2 3
1 2 3
1 2
2 3
输出样例1:
2
输入样例2:
3 2 2
1 1 2
1 2
2 1
输出样例2:
0
来源:https://www.acwing.com/problem/content/description/4078/
2. 思路分析:
因为对于每个询问要求最终的颜色是一样的所以我们需要将这两种球的编号放在一个集合中(例如1,2和2,3要求颜色是一样的那么最终1,2,3颜色最终是一样的),这样最终一定会形成若干个集合,每一个集合中的颜色最终是一样的,集合与集合之间的颜色互不干扰,因为涉及到合并元素到集合中的操作,所以我们可以使用并查集合并元素到一个集合中,对于每一个询问使用并查集的查找与合并元素的操作将元素合并到一个集合中,然后枚举编号为1~n的所有气球,通过并查集的find操作将当前气球的编号添加到所在的集合中,这样就会形成若干个集合,因为需要使得重新染色的气球数量最少,所以我们需要统计出每一个集合中出现颜色最多的次数,将其余的气球染成这种颜色那么最终所需要染色的气球数量一定最少的,因为每一个集合的颜色都是相互独立的所以最终加上每一个集合中需要染色的数量就是答案。
3. 代码如下:
from typing import List
class Solution:
# 查找x的父节点
def find(self, x: int, fa: List[int]):
if fa[x] != x: fa[x] = self.find(fa[x], fa)
return fa[x]
def process(self):
n, m, k = map(int, input().split())
col = [0] + list(map(int, input().split()))
N = 3 * 10 ** 5
fa = [i for i in range(N)]
for i in range(m):
l, r = map(int, input().split())
a, b = self.find(l, fa), self.find(r, fa)
fa[a] = b
S = [list() for i in range(n + 10)]
for i in range(1, n + 1):
# 将每一个节点合并到对应的集合中方便后面操作
S[self.find(i, fa)].append(i)
res = 0
for i in range(1, n + 1):
if S[i]:
# count用来计算当前集合中每一种气球颜色出现的次数
count = dict()
t = 0
for x in S[i]:
if col[x] not in count:
count[col[x]] = 1
else:
count[col[x]] += 1
# 更新当前出现次数最多的颜色对应的出现次数
t = max(t, count[col[x]])
# 将其余气球染成当前的颜色
res += len(S[i]) - t
return res
if __name__ == '__main__':
print(Solution().process())