题意:
有n只颜色的袜子,有m天,每天穿l,r俩个编号的袜子,如果袜子的颜色不同,将其中一只染色为同一色,问最少染多少次?
思路:用并查集将每天穿的袜子合并,然后将每个集合中的一些袜子染色成同一颜色最多的袜子的那个颜色,即为最少染的次数
(1)用vector将每个集合的袜子存放起来
(2)遍历vector,用map集合记录集合中同种颜色袜子的个数,(此处不能用数组计数,因为初始化多了会超时!)
(3)将每个集合中的总袜子数-颜色最多袜子数 都累加起来即为最终染色次数!
很好的一道并查集题目,第一次用并查集解决问题。先前我只知道并查集的模板,但还不会真正的使用并查集。 因为同一天的两只袜子一定要是相同颜色的,那么我们可以遍历一遍每天的两只袜子的序号,把他们归到一类中。然后用ve[i].push_back(a[i]),来存储每一个集合根部结点连接的各种颜色。然后通过map求每个集合中各种颜色袜子的个数,将每个集合中的总袜子数-颜色最多袜子数 都累加起来即为最终染色次数。
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define For(a,b) for(int a=0;a<b;a++)
#define mem(x) memset(x,0,sizeof(x))
#define Debug(x) cout<<"----->"<<x<<endl;
#define sf scanf
#define pf printf
int gcd(int a,int b){ return b>0?gcd(b,a%b):a;}
typedef long long ll;
typedef pair<int ,int > P;
//head
#define maxn 300010
int n,m,k; //n socks,m days,3 kinds 0f color
int a[maxn],pa[maxn],rank[maxn],x,y,z;
vector<int> ve[maxn];
void make_set(int x){
rank[x]=0;
pa[x]=x;
}
int find_set(int x){
if(x!=pa[x]) return pa[x]=find_set(pa[x]);
return pa[x];
}
void union_set(int x,int y){
x=find_set(x);
y=find_set(y);
if(x==y) return ;
if(rank[x]>rank[y]) {
pa[y]=x;
}
else {
pa[x]=y;
rank[y]++;
}
}
int main(){
cin>>n>>m>>k;
for(int i=1;i<=n;i++){
pa[i]=i;
rank[i]=0;
}
for(int i=1;i<=n;i++) cin>>a[i]; //各种袜子的颜色
for(int i=1;i<=m;i++){
cin>>x>>y;
union_set(x,y);
}
//遍历一遍,找到每一个袜子所属于的集合,把它对应的颜色存在ve[]中
for(int i=1;i<=n;i++){
ve[find_set(i)].push_back(a[i]);
}
int ans=0;
for(int i=1;i<=n;i++){
if(ve[i].size()<=1) continue; //把不是集合根结点全过掉
map<int,int> mp;
int maxx=0;
for(int j=0;j<ve[i].size();j++){
mp[ve[i][j]]++; //统计各种颜色的个数
maxx=max(maxx,mp[ve[i][j]]) ;
}
ans+=ve[i].size()-maxx; //分别计算出每个集合的ans,累加
}
cout<<ans<<endl;
return 0;
}