Floyd,传递闭包,并查集(电话圈,uva 247)


n个人(n<=25),打m次电话,若a打给b且b打给a,则说明a,b在同一电话圈内。求出所有电话圈,并输出同一圈内所有人。


n很小,建一个邻接矩阵,MAP[i][j]表示编号为i的人给编号为j的人打过电话。

跑一遍Floyd求出传递闭包。

最后用并查集去分组。


紫书有另一个分组的方法,那就是新建一个图,同一电话圈内的人互连一条边,然后一次输出各个连通分量的所有人。


附上代码

#include<stdio.h>
#include<string>
#include<string.h>
#include<iostream>
#include<map>
#include<vector>
#include<set>
#define maxn 30
using namespace std;

int n,m;
bool MAP[maxn][maxn];
int cnt;
map<string,int>INT;
map<int,string>STR;
vector<set<int> >vec;
int p[maxn];
int done[maxn];

void init()
{
memset(MAP,0,sizeof(MAP));
INT.clear();
STR.clear();
cnt=0;
for(int i=0;i<maxn;i++) p[i]=i;
vec.clear();
memset(done,0,sizeof(done));
set<int>s;
vec.push_back(s);
}

void get()
{
string s1,s2;
for(int i=0;i<m;i++)
{
cin>>s1>>s2;
if(!INT[s1]) {INT[s1]=++cnt;STR[cnt]=s1;}
if(!INT[s2]) {INT[s2]=++cnt;STR[cnt]=s2;}
MAP[INT[s1]][INT[s2]]=true;
}
}

void Floyd()
{
for(int k=1;k<=cnt;k++)
for(int i=1;i<=cnt;i++)
for(int j=1;j<=cnt;j++)
MAP[i][j]=MAP[i][j]||(MAP[i][k]&&MAP[k][j]);
}

int find(int x){return p[x]==x?x:p[x]=find(p[x]);}

void pre()
{
Floyd();
for(int i=1;i<=cnt;i++)
for(int j=1;j<=cnt;j++)
if(i!=j&&MAP[i][j]&&MAP[j][i])
{
int xx=find(i);
int yy=find(j);
if(xx!=yy) p[xx]=yy;
}
for(int i=1;i<=cnt;i++)
{
int id=find(i);
if(!done[id])
{
done[id]=vec.size();
set<int> s;
s.insert(i);
vec.push_back(s);
}
else vec[done[id]].insert(i);
}
for(unsigned int i=1;i<vec.size();i++)
{
set<int>& s=vec[i];
set<int>::iterator it=s.begin();
for(;it!=s.end();it++)
{
if(it!=s.begin()) cout<<", ";
cout<<STR[*it];
}
puts("");
}
}

int kase;

int main()
{
while(scanf("%d %d",&n,&m),n||m)
{
if(kase) puts("");
printf("Calling circles for data set %d:\n",++kase);
init();
get();
pre();
}
}


智能推荐

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
© 2014-2019 ITdaan.com 粤ICP备14056181号  

赞助商广告