给出一些敌人的队,如
a -b
c -d
a-c
给他们分成两组,那么以上就可以分为a,d一组,bc一组。但如果敌人关系是这样的:
a-b
c-d
a-c
b-c
则a-b-c两两为敌人,这样就分不成两组了。
现在就是给出这么一堆关系,让你判断是否可以分成两组。
如何应用并查集呢?思路就是,用一个数组记录敌人的关系,这样可以O(1)时间内找到敌人。
(1)初始化为-1
(2)x-y为敌人,那么enemy[x]若为-1,则更新为y,enemy[y]若为-1则更新为x。
(3)检查x与y的根是否相同,若相同,则说明不能分为两组,程序结束。
(4)若不相同,则x与enemy[y]合并,y与enemy[x]合并。这就是所谓的“敌人的敌人就是朋友”。
程序:
#include <stdio.h>
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <algorithm>
#include <iterator>
using namespace std;
void init(int *p, int n)
{
for(int i=0; i<n; i++)
p[i] = -1;
}
int Find(int x, int *pn)
{
if(pn[x]<0)
return x;
return pn[x] = Find(pn[x], pn);
}
// 合并:大的合并到小的那里
void Union(int x, int y, int *pn)
{
int h1 = Find(x, pn);
int h2 = Find(y, pn);
if(h1<h2) // 合并到小的那里
{
pn[h2] = h1;
pn[h1]--;
}
else if(h1==h2) // 同属一个等价类,则返回
{
return;
}
else
{
pn[h1] = h2;
pn[h2]--;
}
}
const int N = 10000;
// 字符串处理技巧:用一个map来接收,这样的话,可以实现string->int的查询,再将其存入vector,可以实现int->string的查询;
// 注意要include<string>,不是include<string.h>
int main()
{
//初始化并查集
int *parent = new int[N];
int *chou=new int [N]; // 仇人
init(parent, N);
init(chou, N);
// 准备输入
int m, n;
cin>>m; //m 个“关系”
string str1,str2;
map<string,int>ms; // 给出字符串找index
map<string,int>::iterator msi;
vector<string>mv; // 给出index找字符串
int i=0,j=0;
int flag = 0; // 1表示出错
while(i<2*m)
{
// 读入第一个字符串
cin>>str1;
if(ms.count(str1)==0 )
{
ms.insert(pair<string,int>(str1, j));
mv.insert(mv.begin()+j,str1);
j++;
}
i++;
// 读入第二个字符串
cin>>str2;
if(ms.count(str2)==0 )
{
ms.insert(pair<string,int>(str2, j));
mv.insert(mv.begin()+j,str2);
j++;
}
i++;
//处理
msi = ms.find(str1);
int x = msi->second;
msi = ms.find(str2);
int y = msi->second;
// Union(x,y,parent);
if(chou[x]==-1) // 标记出敌人
chou[x]=y;
if(chou[y]==-1)
chou[y]=x;
if(Find(x,parent) == Find(y,parent)) //敌人不可能成为朋友
{
flag=1;
break;
}
else
{ // 敌人的敌人是朋友
Union(x,chou[y],parent);
Union(y,chou[x],parent);
}
}
cout<<"出现过的字符串:"<<endl;
copy(mv.begin(), mv.begin()+j, ostream_iterator<string>(cout, "\n"));
if(flag==1)
cout<<"不可能的组合!"<<endl;
else
cout<<"有可能的组合!"<<endl;
delete []parent;
delete []chou;
return 0;
}
当输入为:
9
a0 a5
a2 a7
a1 a6
a3 a0
a4 a11
a10 a11
a0 a1
a1 a4
a0 a6
时,输出为:
出现过的字符串:
a0
a5
a2
a7
a1
a6
a3
a4
a11
a10
不可能的组合!
输入改为以下,(奇数一组,偶数一组)
8
a0 a5
a2 a7
a1 a6
a3 a0
a4 a11
a10 a11
a0 a1
a1 a4
输出为:
出现过的字符串:
a0
a5
a2
a7
a1
a6
a3
a4
a11
a10
有可能的组合!