https://vjudge.net/problem/UVA-247
题意:如果两个人互相打电话(直接或者间接),则说他们在同一个电话圈里。例如,a打给b,b打给c,c打给d,d打给a,则这四个人在同一个圈里;如果e打给f,而f不打给e,则不能推出e和f在同一个电话圈。输入n(n<=25)个人的m次电话,找出所有的电话圈。人名只包含字母,不超过25个字符,且不重复。
分析:本题一看就是求有向图的强连通分量问题。可以用tarjan解决。但是注意到数据比较小,还可以想到另外一个算法——floyd。在有向图中,有时不必关心路径的长度,只关心每两点之间是否有通路,则可以用1和0表示“连通”和“不连通”。这样,就可以用floyd解决了:dp[i][j]=dp[i][j]||(dp[i][k]&&dp[k][j])。这样的结果称为有向图的传递闭包(Transitive Closure)。
需要注意的地方:
1.变量每次都要初始化,比如vs刚才忘记了
2.必须严格按照输入输出,比如刚才忘了输出时候的逗号后面空格
3.思路要严谨,刚才养了输出时候的内循环判断cir【j】
Folyd模板可用!
#include<vector>
#include<algorithm>
#include<string>
#include<iostream>
#include<map>
#include<stdio.h>
#include<cstring>
using namespace std;
#define maxn 30
vector<string> vs;
struct Edge{
int from,to,dist;
};
struct Floyd{
int n,m;
int d[maxn][maxn],p[maxn];
vector<int> G[maxn];
void init(int n)
{
this->n=n;
for(int i=0;i<n;i++)
{
memset(d[i],0,sizeof(d[i]));
}
}
void AddEdge(int from,int to,int dist)
{
d[from][to]=dist;
m++;
}
void floyd()
{
for(int k=0;k<n;k++)
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
d[i][j]=d[i][j]||(d[i][k]&&d[k][j]);
}
}
void print()
{
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
cout<<d[i][j];
}
cout<<endl;
}
}
};
int tofind(string s)
{
int i;
for(i=0;i<vs.size();i++)
{
if(s==vs[i])
{
return i;
}
}
if(i==vs.size())
{
vs.push_back(s);
return i;
}
}
Floyd solver;
int main()
{
int n,m,icount=1;;
cin>>n>>m;
while(!(!m&&!n))
{
solver.init(n);
vs.clear();
for(int i=0;i<m;i++)
{
string s1,s2;
cin>>s1;
cin>>s2;
int i1=tofind(s1);
int i2=tofind(s2);
solver.AddEdge(i1,i2,1);
}
solver.floyd();
//solver.print();
int cir[maxn]={0};
cout<<"Calling circles for data set "<<icount++<<":"<<endl;
for(int i=0;i<n;i++)
{
if(cir[i]) continue;
string s1=vs[i]+", ";
for(int j=i+1;j<n;j++)
{
if(cir[j]) continue;
if(solver.d[i][j]&&solver.d[j][i])
{
s1+=vs[j]+", ";
cir[j]=1;
}
}
cout<<s1.substr(0,s1.length()-2)<<endl;
}
cin>>n>>m;
if(!(!m&&!n))
cout<<endl;
}
}