计蒜客 商业信息共享 (tarjan,加最少的边构成强联通图)

题目链接:

计蒜客商业信息共享

题目大意:

1.用最少的公司,将所有的信息传递给其他的公司。

2.最少加多少条边,使图变成强联通图。

题目思路:

第一个问题:只要统计出新图中入度为0的点即可。

第二个问题:不难发现,在有向无环图中,要实现对于任意点都可到达其他剩余点,需要添加边的数量 = max(max(入度为 0 点的个数,出度为 0 点的个数))

注意的点:

在计算第二个问题的时候,要考虑此图为强联通图的情况!!!如果为强联通图直接输出0!!!

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
vector<int>v[maxn];//邻接表,存原图
vector<int>scc[maxn];//缩点后的图
stack<int>s;
int dfn[maxn],low[maxn],tot,ins[maxn];
//dfn是否为0可以判断点是否访问过,ins数组用来判断点是否在栈中
//dfn数组表示顶点dfs的时间戳,low[]为u能够追溯到的最早的栈中顶点的次序号
int scc_cnt;//强联通分量的个数
int sccnum[maxn];//缩点数组,表示某个点对应的缩点值
int in[maxn],out[maxn];//出度入度

int n,m;
void readin()
{
    int x,y;
    tot=0;
    scc_cnt=0;
    fill(ins,ins+maxn,0);
    fill(sccnum,sccnum+maxn,0);
    fill(in,in+maxn,0);
    fill(out,out+maxn,0);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) v[i].clear();

    for(int i=1;i<=n;i++){
        while(scanf("%d",&x)){
            if(x==0) break;
            v[i].push_back(x);
        }
    }
}
void tarjan(int x)
{
    low[x]=dfn[x]=++tot;//
    s.push(x);ins[x]=1;
    for(int i=0;i<v[x].size();i++){
        int p=v[x][i];
        if(!dfn[p]){//判断点是否被访问过
            tarjan(p);
            low[x]=min(low[x],low[p]);
        }
        else if(ins[p]) low[x]=min(low[x],dfn[p]);//判断点是否在栈中
    }

    if(low[x]==dfn[x]){//dfs到叶子节点,开始判断,如果==就到达了某个强联通分量的根节点,dfs回溯到了这个点
        scc_cnt++;
        scc[scc_cnt].clear();
        while(1){//得出某个强联通分量的集合
            int now=s.top();
            s.pop();
            ins[x]=0;//出栈ins置0
            if(sccnum[now]!=scc_cnt) scc[scc_cnt].push_back(x);
            sccnum[now]=scc_cnt;//将该连通分量中的每个点都赋予相同的值
            if(now==x) break;//通过栈找到那个点
        }
    }
}

int main()
{
        readin();
        for(int i=1;i<=n;i++){
            if(!dfn[i]){
                tarjan(i);
            }
        }

        //出度入度的统计方法
        for(int u=1;u<=n;u++){
            for(int i=0;i<v[u].size();i++){
                int uu=v[u][i];
                if(sccnum[u]!=sccnum[uu]){
                    in[sccnum[uu]]++;
                    out[sccnum[u]]++;
                }
            }
        }


        int sum1=0;
        for(int i=1;i<=scc_cnt;i++){
            if((out[i]!=0&&in[i]==0)||(in[i]==0&&out[i]==0))//解决第一个问题
                sum1++;
        }
        
        int inz=0,outz=0;
        for(int i=1;i<=scc_cnt;i++){
            if(in[i]==0) inz++;
            if(out[i]==0) outz++;
        }

        int sum2=max(inz,outz);
        if(scc_cnt==1) sum2=0;
        cout<<sum1<<endl<<sum2<<endl;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值