ZOJ-#3630 Information(Trjan+强连通分量数+枚举删点)

        题目大意:给出有向图的关系,求最大连通分量中点数,删去某一个点后,求最大点数的最小值是多少。

        解题思路:直接枚举删点,枚举每一个点,记录最大连通分量中的点数,求最大值中的最小值即可。是tarjan模板题,详见code。

       题目来源:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4776

       code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <stack>
#include <algorithm>
using namespace std;

const int MAXN = 100+10;
int n,m,u,v,now;
int pre[MAXN],lowlink[MAXN],sccno[MAXN],dfs_clock,scc_cnt,temp;
vector<int> g[MAXN];
stack<int> s;

void Tarjan(int u){
    pre[u]=lowlink[u]=++dfs_clock; //时间戳++
    s.push(u); //该点入栈
    for(int i=0;i<g[u].size();i++){ //枚举每一条边
        int v=g[u][i]; //邻接边
        if(v==now) continue; //为枚举点,则不访问
        if(!pre[v]){ //未被访问
            Tarjan(v); //继续向下找
            lowlink[u]=min(lowlink[u],lowlink[v]);//更新结点v所能到达的最下次数层
        }
        else if(!sccno[v]){//如果结点在栈内
            lowlink[u]=min(lowlink[u],pre[v]);
        }
    }
    int ttemp;
    if(lowlink[u]==pre[u]){ //如果结点u是强连通分量的根
        scc_cnt++; //连通分量标号++
        ttemp=0; //记录连通分量中点的数量
        while(!s.empty()){
            int x=s.top(); s.pop(); //取出栈顶元素
            ttemp++;
            sccno[x]=scc_cnt; //出栈结点x属于scc_cnt标号的强连通分量
            if(x==u) break; //回到根节点则break
        }
        if(ttemp<2) ttemp=0; //只有两个点则置为0
        if(temp<ttemp) temp=ttemp; //记录最大的连通分量中的点数
    }
}

int slove(){
    int ans=200,tans;
    for(int i=0;i<n;i++){ //枚举删点
        tans=0;now=i; //各种初始化
        memset(sccno,0,sizeof(sccno));
        memset(pre,0,sizeof(pre));
        for(int j=0;j<n;j++){
            if(!pre[j] && j!=i){
                dfs_clock=scc_cnt=0;temp=0;//初始化连通分量计数器和时间戳
                while(!s.empty()) s.pop(); //每次清空栈
                Tarjan(j);
                if(tans<temp) tans=temp; //最大连通块
            }
        }
        if(ans>tans) ans=tans; //最大中的最小值
    }
    return ans;
}

int main(){
    //freopen("input.txt","r",stdin);
    while(~scanf("%d%d",&n,&m)){
        for(int i=0;i<n;i++) g[i].clear();//使用前清空
        for(int i=0;i<m;i++){ //接受图
            scanf("%d%d",&u,&v);
            g[u].push_back(v);
        }
        printf("%d\n",slove());
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值