入门OJ:郭嘉的消息传递

题目描述

我们的郭嘉大大在曹操这过得逍遥自在,但是有一天曹操给了他一个任务,在建邺城内有N(<=1000)个袁绍的奸细 将他们从1到N进行编号,同时他们之间存在一种传递关系,即若C[i,j]=1,则奸细i能将消息直接传递给奸细j。 现在曹操要发布一个假消息,需要传达给所有奸细,而我们的郭嘉大大则需要传递给尽量少的奸细使所有的奸细都 知道这一个消息,问我们至少要传给几个奸细

输入格式

第一行为N

第二行至第N+1行为N*N的矩阵(若第I行第J列为1,则奸细I能将消息直接传递给奸细J,若第I行第J列为0,则奸细I不能将消息直接传递给奸细J)。

输出格式

输出文件只有一行:即我们的郭嘉大大首先至少要传递的奸细个数。


很基础的强连通分量的题。

首先,我们按照题目给出的传递关系建出一个有向图,然后会得到这样一个性质:对于一个强连通分量,只需要内部的一个人知道那么整个强连通分量里的人都可以得到消息。而如何让强连通分量里的人得到消息呢?有两种办法,第一种是从其它强连通分量里面得到,第二种是我们给它。由于题目要求,我们显然让能通过第一种办法得到消息的强连通分量都用第一种办法得到,再考虑得不到的强连通分量。

怎么判断能不能通过第一种办法得到消息呢?显然,我们把整个图进行强连通分量缩点之后,入度为0的强连通分量肯定是不能从其它强连通分量得到消息的。所以我们要把消息传给入度为0的强连通分量。然后入度不为0的,就可以通过第一种办法得到了。所以本题的答案就是——入度为0的强连通分量数。通过经典Tarjan算法可以O(N+M)求出强连通分量。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#define maxn 1001
using namespace std;
 
vector<int> to[maxn];
int dfn[maxn],low[maxn],tot;
int col[maxn],ind[maxn],cnt;
int stack[maxn],top;
bool instack[maxn];
int n,ans;
 
void tarjan(int u){
    dfn[u]=low[u]=++tot;
    stack[++top]=u,instack[u]=true;
    for(register int i=0;i<to[u].size();i++){
        int v=to[u][i];
        if(!dfn[v]) tarjan(v),low[u]=min(low[u],low[v]);
        else if(instack[v]) low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u]){
        int v; cnt++;
        do{ v=stack[top--],instack[v]=false; col[v]=cnt; }while(v!=u);
    }
}
 
int main(){
    scanf("%d",&n);
    for(register int i=1;i<=n;i++){
        for(register int j=1;j<=n;j++){
            int tmp; scanf("%1d",&tmp);
            if(tmp) to[i].push_back(j);
        }
    }
    for(register int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
 
    for(register int i=1;i<=n;i++){
        for(register int j=0;j<to[i].size();j++){
            int v=to[i][j];
            if(col[i]!=col[v]) ind[col[v]]++;
        }
    }
    for(register int i=1;i<=cnt;i++) if(!ind[i]) ans++;
    printf("%d
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/akura/p/10945545.html