Network of Schools - tarjan缩点

题目:https://vjudge.net/contest/388498#problem/G

题意:有向图。每个点能把当前信息传达到其指向的点。问题1:至少需要提供给多少个点信息才能让信息传遍所有点;问题2:至少需要补多少条有向边才能,给任意一个点信息能够传遍所有点。

题解:

问题1实际上是问缩点后的图里有多少个入度为0的点。因为非入度为0的点可以从其他点那获得信息,不需要向他提供信息。

问题2实际上是问,缩点后图里入度为0的点数量rd0,出度为0的点的数量cd0。求这两个值的最大值。要实现题目的需求,就是对于任意一点,可以到达所有点。由这个可以推出,任意一点,都可以被其他点到达。等价于,任意一点,其入度和出度都不为0。

新建一条边最佳情况下可以减少一个入度为0的点和一个出度为0的点。所以至少需要max(rd0,cd0)条边。

参考博客:https://blog.csdn.net/weixin_42868863/article/details/102847615

 

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <queue>
#include <stack>
#include <cstring>
#include <map>
#include <set>
#define ALL(x) x.begin(),x.end()
using namespace std;
typedef long long ll;
const ll mx=100+10;
ll low[mx], dfn[mx], cnt;
ll e[mx][mx];
ll n;
void read(){
    cnt=0;
    for(ll i=1;i<mx;i++){
        low[i]=dfn[i]=-1;
        for(ll j=1;j<mx;j++) e[i][j]=-1;
    }
    scanf("%lld", &n);
    for(ll i=1;i<=n;i++){
        ll v;
        while(1){
            scanf("%lld", &v);
            if(v==0) break;
            e[i][v]=1;
        }
    }
}
ll sum;//统计颜色
ll color[mx];
ll vis[mx];//是否在栈中
stack<ll>s;
void tarjan(ll c, ll fa){
    dfn[c]=low[c]=++cnt;
    s.push(c);vis[c]=1;
    for(ll i=1;i<=n;i++){
        if(e[c][i]==-1)continue;
        if(i==fa || i==c) continue;
        if(dfn[i]==-1){//没有经过
            tarjan(i, c);
            low[c]=min(low[c], low[i]);
        }
        else if(vis[i]==1){//已经经过了 i是父亲
            low[c]=min(low[c], dfn[i]);//直接相连的父亲
        }
    }
    if(low[c]==dfn[c]){//自己和子结点形成了强连通 或者只有自己一个人
        color[c]=++sum;
        vis[c]=0;
        while(!s.empty() && s.top()!=c){
            color[s.top()]=sum;
            vis[s.top()]=0;//出栈
            s.pop();
        }
        s.pop();//把c给pop掉
    }
}
ll rd[mx], cd[mx];
ll e2[mx][mx];
void init(){//把color数组转化为数据
    for(ll i=1;i<=n;i++){
        for(ll j=1;j<=n;j++){
            //i->j
            if(e[i][j]==-1 || i==j)continue;
            ll u=color[i], v=color[j];
            if(u==v) continue;
            e2[u][v]=1;
        }
    }
    for(ll i=1;i<=sum;i++){
        for(ll j=1;j<=sum;j++){
            if(i==j)continue;
            if(e2[i][j]==0)continue;
            rd[j]++;cd[i]++;
        }
    }
    ll rd0=0, cd0=0;
    for(ll i=1;i<=sum;i++){
        if(rd[i]==0)rd0++;
        if(cd[i]==0)cd0++;
    }
    if(sum==1){
        printf("1\n0\n");
        return;
    }
    printf("%lld\n%lld\n", rd0, max(rd0, cd0));
}
void solve(){
    read();
    for(ll i=1;i<=n;i++){
        if(dfn[i]==-1) tarjan(i, -1);
    }
    init();
}
int main(){
    solve();
    return 0;
}
View Code

 

posted @ 2021-04-27 19:10  反射狐  阅读(41)  评论(0编辑  收藏  举报