zzuoj下大雪了 - 强连通分量(tarjan或kosaraju)

题目描述

小J在读研究生时,经常给中学的同学们当家教(PS:小J是天枰座,他有特殊的癖好,就是每个中学带的学生一样多,这样能保证教育的“公平性”)。这些同学往往放学后,从学校出发去小J家补课。这天,郑州下了场鹅毛大雪,小J一看,完了,很多同学都无法来自己家上课了。因此,他要选取某所学校作为上课地点,然后通知一部分学校能到的学生来上课。郑州市有N个中学(编号为 1..N),其间有M条道路。下雪后,由于路滑,经常会有车辆出现碰撞、剐蹭,使得路况出现了两种情况,一种为单向通行的,一种为双向通行的,分别用 1 和 2 来标记。如果存在由学校A到达学校 B 的通路,那么我们认为可以从A 到达 B,记为(A,B)。当(A,B)和(B,A)同时满足时,我们记为<A,B>。由于小J在前往预订学校途中,可能会出现意外情况,使得小J可能会临时更改学校,然后抓紧通知这些学生,放学后赶往新地点。因此,这就要求小J计划通知这些学生所在的学校中,任意两个学校X, Y间都满足<X, Y>。现在小J想给最多的学生上课,请你帮他求出计划通知学生所在学校的编号。若存在两个最大的,输出字典序最小的,比如当存在 1,3,4和 2,5,6 这两个最大连通区域时,输出的是 1,3,4。

输入

第 1 行:两个正整数 N,M (N <= 5,000 且 M <= 50,000)

第 2..M+1 行:每行三个正整数 a, b, t。(t = 1 表示存在从学校 a 到 b 的单向道路, t = 2 表示学校a,b 之间存在双向通行的道路。 保证每条道路只出现一次。)

输出

第 1 行: 1 个整数,表示最多学校个数。

第 2 行:若干个整数,依次输出学校编号,用空格隔开。

样例输入

5 5
1 2 1
1 3 2
2 4 2
5 1 2
3 5 1

样例输出

3
1 3 5

 思路:

两种方法:tarjan或kosaraju

kosaraju代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<cmath>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef pair<int,int>P;
const int INF=0x3f3f3f3f;
const int N=5010,mod=32767;
int vis[N],cmp[N],cnt[N];
vector<int>G[N],rG[N];
vector<int>vs;
 
void add(int from,int to){
    G[from].push_back(to);
    rG[to].push_back(from);
}
 
void dfs(int v){
    vis[v]=1;
    for(int i=0;i<G[v].size();i++){
        if(!vis[G[v][i]])dfs(G[v][i]);
    }
    vs.push_back(v);
}
 
void rdfs(int v,int k){
    vis[v]=1;
    cmp[v]=k;
    cnt[k]++;
    for(int i=0;i<rG[v].size();i++){
        if(!vis[rG[v][i]])rdfs(rG[v][i],k);
    }
}
 
int main(){
    int n,m,a,b,c;
    scanf("%d%d",&n,&m);
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&a,&b,&c);
        if(c==1)add(a,b);
        else {add(a,b);add(b,a);}
    }
    for(int i=1;i<=n;i++){
        if(!vis[i])dfs(i);
    }
    memset(vis,0,sizeof(vis));
    int k=0;
    for(int i=vs.size()-1;i>=0;i--){
        if(!vis[vs[i]])rdfs(vs[i],k++);
    }
    int mx=0,num;
    for(int i=1;i<=n;i++){
        int tmp=cmp[i];//属于的强联通分量的值
        if(cnt[tmp]>mx){
            mx=cnt[tmp];
            num=tmp;
        }
    }
    printf("%d\n",mx);
    int tot=0;
    for(int i=1;i<=n;i++){
        if(cmp[i]==num){
            tot++;
            if(tot!=mx)printf("%d ",i);
            else printf("%d\n",i);
        }
    }
}

tarjan算法代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<cmath>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef pair<int,int>P;
const int INF=0x3f3f3f3f;
const int N=5015,M=50005,mod=32767;
struct A{
    int to,next;
}edge[2*M];
int head[N],cnt=0,sta[N],vis[N];
int Q,mx=0,dfn[N],low[N],tot,inde,k;
set<int>qqq[N];
void add(int from,int to){
    edge[cnt].to=to;
    edge[cnt].next=head[from];
    head[from]=cnt++;
}

void tarjan(int x){
    dfn[x]=low[x]=++tot;
    sta[++inde]=x;
    vis[x]=1;
    for(int i=head[x];i!=-1;i=edge[i].next){
        if(!dfn[edge[i].to]){
            tarjan(edge[i].to);
            low[x]=min(low[x],low[edge[i].to]);
        }
        else if(vis[edge[i].to]){
            low[x]=min(low[x],dfn[edge[i].to]);
        }
    }
    if(low[x]==dfn[x]){
        k++;
        int tmp=0;
        do{
            qqq[k].insert(sta[inde]);
            tmp++;
            vis[sta[inde]]=0;
            inde--;
        }while(x!=sta[inde+1]);
        if(mx<tmp){Q=k;mx=tmp;}
    }
    return ;
}

int main(){
    int n,m,a,b,t;
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&a,&b,&t);
        add(a,b);
        if(t==2)add(b,a);
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i])tarjan(i);
    }
    printf("%d\n",mx);
    set<int>::iterator it1,it2=qqq[Q].begin();
    int tmp=*it2;
    for(int i=1;i<=k;i++){
        if(qqq[i].size()==mx){
            it1=qqq[i].begin();
            it2=qqq[Q].begin();
            if((*it1)<(*it2))Q=i;
        }
    }
    it1=qqq[Q].begin();
    printf("%d",*it1);
    it1++;
    for(;it1!=qqq[Q].end();it1++){
        printf(" %d",*it1);
    }
    printf("\n");
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值