Network of Schools POJ - 1236 (强联通)
一些学校连接到了一个计算机网络。网络中的学校间有如下约定:每个学校维护一个列表,当该学校收到软件或信息后将会转发给列表中的所有学校(也就是接收方列表)。需要注意的是如果B学校在A学校的接收方列表中,A学校不一定会出现在B学校的接收方列表中。
你现在的任务是写出一个程序来计算必须收到一份软件来使网络中的所有学校都能收到软件的学校的数量的最小值(此为子任务A)。作为一个远期目标,我们希望给任意一个学校发送一份软件都能使网络中的所有学校都收到软件。为了实现这个目标,我们或许需要在一些学校的接收方列表中添加新项。 你现在需要计算出至少需要添加多少新项才能实现这个远期目标(此为子任务B)。
Input
第一行是一个整数N:计算机网络中的学校数量(2<=N<=100)。接下来有N行数据,接下来的第 i 行描述了第 i 个学校的接收方列表。每行列表以0结尾。如果第 j 行中只有一个0代表第 j 个学校的接收方列表是空的。
Output
你的程序应该在标准输出中输出两行。每一行应为一个整数:第一行为子任务A的解,第二行为子任务B的解。
Sample Input
5 2 4 3 0 4 5 0 0 0 1 0 |
---|
Sample Output
1 2 |
---|
首先缩点,查看有几个强连通图,查看缩点后的DAG(有向无环图)入度为0和出度为0的多少,选取其中最大值。
#include <cstdio>
#include <stack>
#include <cstring>
using namespace std;
const int MAXN=1001;
struct node{
int v,next;
}edge[MAXN*10];
int dfn[MAXN],low[MAXN];
int head[MAXN],cnt,belong[MAXN];
bool vis[MAXN];
int tim;
stack<int >st;
int out[MAXN],in[MAXN];
int num=0;
void tarjan(int u)
{
dfn[u]=low[u]=++tim;
vis[u]= true;
st.push(u);
for (int i = head[u]; i !=-1 ; i=edge[i].next) {
if(!dfn[edge[i].v]) {
tarjan(edge[i].v);
low[u]=min(low[u],low[edge[i].v]);
}
else if(vis[edge[i].v]){
low[u]=min(low[u],dfn[edge[i].v]);
}
}
if(dfn[u]==low[u])
{
int x;
++num;
while(1)
{
x=st.top();
st.pop();
vis[x]=0;
belong[x]=num;
if(x==u)break;
}
}
}
void add(int x,int y)
{
edge[++cnt].next=head[x];
edge[cnt].v=y;
head[x]=cnt;
return ;
}
void slove(int n)
{
for (int i = 1; i <=n ; ++i) {
if(dfn[i]==0) tarjan(i);
}
for (int j = 1; j <=n ; ++j) {
for (int k = head[j]; k !=-1 ; k=edge[k].next) {
int v=edge[k].v;
if(belong[j]!=belong[v]) {
out[belong[j]]++;
in[belong[v]]++;
}
}
}
int ans1=0,ans2=0;
for (int i = 1; i <=num ; ++i) {
if(in[i]==0)ans1++;
if(out[i]==0) ans2++;
}
ans2=max(ans1,ans2);
if(num==1)printf("1\n0\n");
else
printf("%d\n%d\n",ans1,ans2);
}
int main()
{
int n;
scanf("%d",&n);
int x;
cnt=0;
memset(head,-1, sizeof(head));
for (int i = 1; i <=n ; ++i) {
while (scanf("%d",&x)&&x) {
add(i,x);
}
}
slove(n);
return 0;
}