在讲解这个题目之前, 我不得不狠狠的吐槽cin和cout的效率, 我提交了6遍都是超时, 最后一遍提交时统统把cin和cout改为scanf和printf才过的, 当时心情又高兴又难受.
查看题目点击这里 Find them, Catch them POJ - 1703
吐槽完了, 开始讲题.
第一次遇见这种题目是感觉满头痛的, 咦~, 并查集不是将关系是朋友的联系在一起吗? 可没学过把敌人分开的呀! (题目是我在并查集专题遇见的, 所以一开始我知道是要并查集的).
是的 , 我们没学过将敌人分开, 但可以转化思路, 正所谓敌人的敌人就是朋友, 我们可以将a和 b的敌人结为朋友不就化成我们平时所熟悉的题型了吗 (因为只有两大帮派, 所以不是朋友就是敌人)( enemy[b]指的就是b的敌人)
在这里就不讲解并查集的原理了, 默认大家都会并查集, 如果对并查集不熟, 强烈推荐看看我的另一篇转载的文章, 是讲解并查集的 <有趣的并查集详解>
这里就直接附上代码, 解释注释部分够了
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAX = (int)1e5 + 5;
int per[MAX],enemy[MAX];
int find(int x) //查找根节点
{
int r = x;
while (r != per[r])
r = per[r];
int i = x, j;
while (i != r) //压缩路劲, 这段不加上应该没什么大问题
{
j = per[i];
per[i] = r;
i = j;
}
return r;
}
void join(int x, int y) //x,y结成朋友
{
int fx = find(x), fy = find(y);
if (fx != fy)
per[fx] = fy;
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
per[i] = i;
enemy[i] = 0; //0 代表没有敌人
}
//memset(enemy, 0, sizeof(enemy));//0代表没有敌人
getchar(); //别忘加这个
while (m--)
{
char c;
int a, b;
scanf("%c%d%d", &c, &a, &b);
//cin >>c >> a >> b;
getchar(); //别忘了加这个
if (c == 'D')
{
if (enemy[a] == 0 && enemy[b] == 0)//如果原来a,b都没有敌人
{
enemy[a] = b; enemy[b] = a;//那么现在a,b就是敌人
}
else if (enemy[a] == 0) //如果a没有敌人, 而b有敌人
{
enemy[a] = b;//那么现在a的敌人就是b;
join(a, enemy[b]); //a和b是敌人,所以a和b的敌人enemy[b]就是朋友
}
else if (enemy[b] == 0) //如果b没有敌人, a有敌人
{
enemy[b] = a; //那么现在b的敌人就是a
join(b, enemy[a]); //b和a是敌人,a和enemy[a]是敌人,所以b和enemy[a]是朋友
}
else//a,b都有敌人
{
join(a, enemy[b]);
join(b, enemy[a]);
}
}
else if (c == 'A')
{
if (find(a) == find(b)) //如果a,b有相同的根节点,则a,b是朋友
//cout << "In the same gang." << endl;
printf("%s\n", "In the same gang.");
//如果a和b的敌人在一起 或者 b和a的敌人在一起 就认为a,b是敌人 ---- 这里不可以写成 find(a)!=find(b)
else if (find(a) == find(enemy[b]) || find(b) == find(enemy[a]))
//cout << "In different gangs." << endl;
printf("%s\n", "In different gangs.");
else
//cout << "Not sure yet." << endl;
printf("%s\n", "Not sure yet.");
}
}
}
return 0;
}