Network of Schools POJ1236(tarjan缩点+强连通分量模板)

来源:互联网 发布:在淘宝网上卖东西 编辑:程序博客网 时间:2024/05/21 17:17
Network of Schools
Time Limit: 1000MS Memory Limit: 10000KTotal Submissions: 19564 Accepted: 7705

Description

A number of schools are connected to a computer network. Agreements have been developed among those schools: each school maintains a list of schools to which it distributes software (the “receiving schools”). Note that if B is in the distribution list of school A, then A does not necessarily appear in the list of school B 
You are to write a program that computes the minimal number of schools that must receive a copy of the new software in order for the software to reach all schools in the network according to the agreement (Subtask A). As a further task, we want to ensure that by sending the copy of new software to an arbitrary school, this software will reach all schools in the network. To achieve this goal we may have to extend the lists of receivers by new members. Compute the minimal number of extensions that have to be made so that whatever school we send the new software to, it will reach all other schools (Subtask B). One extension means introducing one new member into the list of receivers of one school. 

Input

The first line contains an integer N: the number of schools in the network (2 <= N <= 100). The schools are identified by the first N positive integers. Each of the next N lines describes a list of receivers. The line i+1 contains the identifiers of the receivers of school i. Each list ends with a 0. An empty list contains a 0 alone in the line.

Output

Your program should write two lines to the standard output. The first line should contain one positive integer: the solution of subtask A. The second line should contain the solution of subtask B.

Sample Input

52 4 3 04 5 0001 0

Sample Output

12

Source

IOI 1996

题目大意:

一些学校连成了网络, 在学校之间存在某个协议:每个学校都维护一张传送表,表明他们要负责将收到的软件传送到表中的所有学校。如果A在B的表中,那么B不一定在A的表中。

    现在的任务就是,给出所有学校及他们维护的表,问1、如果所有学校都要被传送到,那么需要几份软件备份;2、如果只用一份软件备份,那么需要添加几条边?

解题思路:对于第一个问题来说,需要算出来一共有多少个强连通分量,然后缩点形成一个图,寻找一共有几个入度为0的点,然后对于第二问来说,需要做的是只要算出入度为0和出度为0的点的个数最大值即可

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#include <vector>#include <queue>#include <set>#include <map>#include <string>#include <cmath>#include <cstdlib>#include<stack>#include <ctime>using namespace std;typedef long long LL;int check[105],low[105];vector<int> tu[105];int index,instack[105],ans[105],a[105],fllow[105],sum;stack<int> st;int inn[105],outt[105];//check[u]数组表示节点u搜索的次序编号时间戳。low[u]为u能追溯到的最早的时间戳void tarjan(int x){check[x]=low[x]=++index;//初始化搜索到的节点xst.push(x);//将x点放在求强连通分量的栈中instack[x]=1;//x点是否在栈中for(int i = 0;i<tu[x].size();i++){int k=tu[x][i];if(!check[k]){tarjan(k);low[x]=min(low[x],low[k]);//比较当前点x能够到达的最早时间戳,与他的子树中的点的最早时间戳比较}else{if(instack[k])//如果x为根的子树中有K在栈中{low[x]=min(low[x],check[k]);//比较k的时间戳大小}}}if(check[x]==low[x])//x点为根节点{sum++;int k=0;while(k!=x)//直到符合条件前,这些点都是强连通分量{k=st.top();st.pop();fllow[k]=sum;//将这些点分组进行标记instack[k]=0;ans[sum]=x;}}}int main(){int i,n,x;cin>>n;for(i=1;i<=n;i++){for(;;){cin>>x;if(x==0)break;tu[i].push_back(x);a[x]++;//}}int cnt=0;index=0;for(i=1;i<=n;i++)if(!check[i])//存在多个图tarjan(i);int t1,t2;t1=t2=0;int j;for(i=1;i<=n;i++){for(j=0;j<tu[i].size();j++){int k=tu[i][j];if(fllow[k]!=fllow[i])//{inn[fllow[k]]++;outt[fllow[i]]++;}}}for(i=1;i<=sum;i++){if(inn[i]==0){t1++;}if(outt[i]==0){t2++;}}if(sum==1){cout<<1<<endl;cout<<0<<endl;return 0;}cout<<t1<<endl;cout<<max(t1,t2)<<endl;return 0;}


阅读全文
0 0
原创粉丝点击