C - Socks
题意为:
Arseniy的妈妈要外出m天,给他准备了n只袜子,并且备注好了第几天穿哪两只袜子。但Arseniy发现,如果按照妈妈的备注穿袜子,会有一些天穿不同颜色的袜子,穿出去会被耻笑。于是,他准备了k种颜料,如果袜子颜色不相同,他会涂成相同颜色。他想知道最少改变几只袜子的颜色,穿出去不会被人笑话。
输入:
n, m, k
接下来一行有n个数,代表n个袜子的颜色
接下来有m行,代表每一天需要穿第几只袜子
输出:
the minimum number of socks that should have their colors changed
他需要改变的最少袜子数
#include <stdio.h>
#include <string.h>
#include <map>
#define N 200005
using namespace std;
map<int, int>tree[N];
int f[N]={0};//并查集祖先
int cnt[N];//名下的子孙个数
int getf(int t)//找祖先
{
if (f[t] == t)
return t;//祖先就是自己本身
else
{
f[t] = getf(f[t]);//继续往前找,直到找到祖先
return f[t];//返回祖先
}
}
void merge(int l, int r)//合并
{
int t1, t2;
t1 = getf(l);//找祖先
t2 = getf(r);
if (t1 != t2)//不是同一个祖先
{
f[t2] = t1;//靠左原则,把左边变为右边祖先,即t2的祖先是t1
cnt[t1] += cnt[t2];//把以t2为祖先的数全部加入t1名下
}
return ;
}
int main()
{
int n, m, k, i, l, r, vis[N];
int max[N];//同一祖先下颜色相同袜子的最多数
int color[N];
memset(max, 0, sizeof(max));
memset(vis, 0, sizeof(vis));
scanf("%d %d %d", &n, &m, &k);
for (i = 1; i<= n; i++)
{
scanf("%d", &color[i]);
f[i] = i;
cnt[i] = 1;
}
for (i = 1; i <= m; i++)
{
scanf("%d %d", &l, &r);
merge(l, r);
}
for (i = 1; i <= n; i++)
{
int root = getf(i);//祖先
tree[root][color[i]]++;//同一祖先下的袜子的不同颜色的个数
if (tree[root][color[i]] > max[root])
max[root] = tree[root][color[i]];
}
int sum = 0;
for (i = 1; i<= n; i++)
{
int root = getf(i);
if (!vis[root])
{
vis[root] = 1;
sum += cnt[root] - max[root];
}
}
printf("%d\n", sum);
return 0;
}
用并查集:将每天有联系的袜子(就是可能在同一天穿的袜子)构成树,然后组成森林,然后在一棵树上染袜子,将所有的袜子染成这个树上颜色
最多的那种颜色,然后累加
例子:
“`sequence
子树1——–>(1【1】,3【1】,5【2】,6【2】,7【3】)
子树2——–>(8【1】,9【1】,10【1】,2【2】,4【2】)
注释:
【】里为袜子颜色
tree[1][1] = 2; //有2个1号颜色的袜子
tree[1][2] = 2; //有2个2号颜色的袜子
tree[1][3] = 1; //有1个3号颜色的袜子
//以1为祖先的子树中袜子的颜色分布
tree[8][1] = 3; //有3个1号颜色的袜子
tree[8][2] = 2; //有2个2号颜色的袜子
tree[8][3] = 0;
//以8为祖先的子树中袜子的颜色分布
max[1] = 2; //代表以1为祖先的子树中,相同颜色袜子的个数最多为2
max[8] = 3; //代表以8为祖先的子树中,相同颜色袜子的个数最多为3
cnt[1] = 5; //以1为祖先的子树有5个孩子
cnt[8] = 5; //以8为祖先的子树有5个孩子