UVa247电话圈(强联通分量--传递闭包+并查集 or tarjan算法)

If you’ve seen television commercials for long-distance phone companies lately, you’ve noticed thatmany companies have been spending a lot of money trying to convince people that they provide thebest service at the lowest cost. One company has “calling circles.” You provide a list of people thatyou call most frequently. If you call someone in your calling circle (who is also a customer of the samecompany), you get bigger discounts than if you call outside your circle. Another company points outthat you only get the big discounts for people in your calling circle, and if you change who you callmost frequently, it’s up to you to add them to your calling circle.LibertyBell Phone Co. is a new company that thinks they have the calling plan that can put othercompanies out of business. LibertyBell has calling circles, but they figure out your calling circle foryou. This is how it works. LibertyBell keeps track of all phone calls. In addition to yourself, yourcalling circle consists of all people whom you call and who call you, either directly or indirectly.For example, if Ben calls Alexander, Alexander calls Dolly, and Dolly calls Ben, they are all withinthe same circle. If Dolly also calls Benedict and Benedict calls Dolly, then Benedict is in the samecalling circle as Dolly, Ben, and Alexander. Finally, if Alexander calls Aaron but Aaron doesn’t callAlexander, Ben, Dolly, or Benedict, then Aaron is not in the circle.You’ve been hired by LibertyBell to write the program to determine calling circles given a log ofphone calls between people.InputThe input file will contain one or more data sets. Each data set begins with a line containing twointegers, n and m. The first integer, n, represents the number of different people who are in the dataset. The maximum value for n is 25. The remainder of the data set consists of m lines, each representinga phone call. Each call is represented by two names, separated by a single space. Names are first namesonly (unique within a data set), are case sensitive, and consist of only alphabetic characters; no nameis longer than 25 letters.For example, if Ben called Dolly, it would be represented in the data file asBen DollyInput is terminated by values of zero (0) for n and m.OutputFor each input set, print a header line with the data set number, followed by a line for each callingcircle in that data set. Each calling circle line contains the names of all the people in any order withinthe circle, separated by comma-space (a comma followed by a space). Output sets are separated byblank lines.Sample Input5 6Ben AlexanderAlexander DollyDolly BenDolly BenedictBenedict DollyAlexander Aaron14 34John AaronAaron BenedictBetsy JohnBetsy RingoRingo DollyBenedict PaulJohn BetsyJohn AaronBenedict GeorgeDolly RingoPaul MarthaGeorge BenAlexander GeorgeBetsy RingoAlexander StephenMartha StephenBenedict AlexanderStephen PaulBetsy RingoQuincy MarthaBen PatrickBetsy RingoPatrick StephenPaul AlexanderPatrick BenStephen QuincyRingo BetsyBetsy BenedictBetsy BenedictBetsy BenedictBetsy BenedictBetsy BenedictBetsy BenedictQuincy Martha0 0Sample OutputCalling circles for data set 1:Ben, Alexander, Dolly, BenedictAaronCalling circles for data set 2:John, Betsy, Ringo, DollyAaronBenedictPaul, George, Martha, Ben, Alexander, Stephen, Quincy, Patrick

【题意】

有n个人,m次单向通话。一个电话圈指,圈内人员,均可直接或间接的两两联系到。求出所有的电话圈

【分析】

方法1:tarjan算法。直接套模板,分离各强连通分量即可。

方法2:刘汝佳分析,用floyd算法求出有向图的传递闭包(可百度)。然后通过闭包,得知任意的i,j是否属于同一个强联通分量,并查集维护。

【代码--传递闭包+并查集】

#include<bits/stdc++.h>
using namespace std;
vector<string>V;
map<string,int>M;
int fa[200];
int ffind(int x){return fa[x]==x?x:fa[x]=ffind(fa[x]);}
bool join(int x,int y){x=ffind(x);y=ffind(y);if(x!=y)fa[x]=y;}
int main()
{
    int Map[30][30],n,m,t=0;
    while(cin>>n>>m,m)
    {
        memset(Map,0,sizeof(Map));
        for(int i=1;i<=n;i++)fa[i]=i;
        V.clear();
        M.clear();
        while(m--)
        {
            string a,b;
            cin>>a>>b;
            if(!M.count(a)){
                V.push_back(a);
                M[a]=V.size();
            }
            if(!M.count(b)){
                V.push_back(b);
                M[b]=V.size();
            }
            Map[M[a]][M[b]]=1;
        }
        for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) //传递闭包
        {
            Map[i][j]|=Map[i][k]&Map[k][j];
        }
        for(int i=1;i<=n;i++)
        for(int j=1;j<i;j++)
            if(Map[i][j]&Map[j][i])join(i,j);
        if(t++)printf("\n");
        printf("Calling circles for data set %d:\n",t);
        for(int i=1;i<=n;i++)if(fa[i]==i)
        {
            cout<<V[i-1];
            for(int j=1;j<=n;j++)if(i!=j&&ffind(j)==i)
                cout<<", "<<V[j-1];
            cout<<endl;
        }
    }
}

【代码--tarjan】

#include<bits/stdc++.h>
using namespace std;
const int MAX=202020;
struct node{
    int s,t,next;
}e[MAX];
int head[MAX],cnt;
void add(int u,int v)
{
    e[cnt]=node{u,v,head[u]};
    head[u]=cnt++;
}
int dfn[MAX];//每个节点的访问时间编号
int low[MAX];//每个点能到达的最小编号
int sta[MAX],top;
int Scc[MAX];//每个点所属的分量 序号
int in[MAX],out[MAX];
void tardfs(int u,int &lay,int &sig)
{
    low[u]=dfn[u]=lay++;//到达此点的时间
    sta[top++]=u;//压入栈
    for(int i=head[u];~i;i=e[i].next)
    {
        int t=e[i].t;
        if(dfn[t]==0)
        {
            tardfs(t,lay,sig);
            low[u]=min(low[u],low[t]);
        }
        else if(!Scc[t])//访问过了
            low[u]=min(low[u],dfn[t]);//强连通求法可以用low
    }
    if(low[u]==dfn[u])//u不能到达任何之前走过的点
    {
        sig++;
        while(1)
        {
            int j=sta[--top];//出栈
            Scc[j]=sig;
            if(j==u)break;
        }//包含u的连通分量出栈
    }
}
int tarjan(int n)
{
    int sig=0;//强连通数
    int lay=1;//时间戳
    top=0;
    memset(Scc,0,sizeof(Scc));
    memset(dfn,0,sizeof(dfn));//时间戳归0
    for(int i=1;i<=n;i++)
    {
        if(!dfn[i])tardfs(i,lay,sig);
    }
    return sig;//返回连通数
}
int n,m;
vector<string>V,ans[27];
map<string,int>M;
bool cmp(vector<string>a,vector<string>b)
{
    return a[0]<b[0];
}
int main()
{
    int t=0;
    while(cin>>n>>m,n)
    {
        for(int i=0;i<=n;i++)ans[i].clear();
        V.clear();
        M.clear();
        memset(head,-1,sizeof(head));
        cnt=0;
        while(m--)
        {
            string a,b;
            cin>>a>>b;
            if(!M.count(a)){
                V.push_back(a);
                M[a]=V.size();
            }
            if(!M.count(b)){
                V.push_back(b);
                M[b]=V.size();
            }
            add(M[a],M[b]);
        }
        int sig=tarjan(n);
        for(int i=0;i<V.size();i++)
            ans[Scc[M[V[i]]]].push_back(V[i]);
        //sort(ans+1,ans+n+1);
        if(t++)printf("\n");
        printf("Calling circles for data set %d:\n",t);
        for(int i=1;i<=n;i++)if(ans[i].size())
        {
            for(int j=0;j<ans[i].size();j++)
            {
                if(j)printf(", ");
                cout<<ans[i][j];
            }
            cout<<endl;
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雪的期许

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值