Network of Schools
Time Limit: 1000MS Memory Limit: 10000K
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
5
2 4 3 0
4 5 0
0
0
1 0
Sample Output
1
2
Source
IOI 1996
题意:学校连接到一个计算机网络,这些学校之间达成一个协议,每一个学校维护着一个学校的列表,可以向学校列表中的学校发布软件。
任务A:计算为了使每一个学校都能通过网络收到软件,至少需要准备多少份软件拷贝
任务B:要想确保在任意一个学校发放一个新的软件拷贝,所有的学校都可以接受到。必须在列表中增加新的成员,计算需要增加新成员的数目。思路:这是一个有向图,则在图中可能存在强连通分量,强连通分量中是相互连接,所以需要缩点,缩点以后会形成一个DAG图,对于一个DAG图我们只需要在入度为零的点都放上软件就可以使图中的所有点都收到软件,所以ansA为入度为零的点的数目,对于任务B则是将DAG图构成一个强连通分量,最明显的方式就是将出入为零的点与入度为零的点相连,这样就可以使DAG图变成强连通的,而增加的边的数目则要是出度为零点的数目与入度为零点的数目的最大值(自己画画就知道了)
Tarjan
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <queue>
#include <stack>
#include <set>
#include <algorithm>
using namespace std;
const int Max = 110;
vector <int> Map[Max];
int dfn[Max],RDu[Max],CDu[Max],low[Max],dep;
int n,num,pre[Max],vis[Max];
stack<int>S;
void init()
{
for(int i=0;i<=n;i++)
{
Map[i].clear();
}
memset(dfn,-1,sizeof(dfn));
memset(RDu,0,sizeof(RDu));
memset(CDu,0,sizeof(CDu));
memset(vis,0,sizeof(vis));
dep = 0 ; num = 0;
}
void Tarjan(int u)//求强连通分量
{
low[u]=dfn[u]=dep++;
S.push(u);
vis[u]=1;
for(int i=0;i<Map[u].size();i++)
{
if(vis[Map[u][i]]==0)
{
Tarjan(Map[u][i]);
low[u] = min(low[u],low[Map[u][i]]);
}
if(vis[Map[u][i]]==1)
{
low[u] = min(low[u],dfn[Map[u][i]]);
}
}
if(dfn[u]==low[u])
{
while(!S.empty())
{
int v = S.top();
S.pop();
pre[v] = num;
vis[v]=2;
if(v==u)
{
break;
}
}
num++;
}
}
int main()
{
while(~scanf("%d",&n))
{
init();
int v;
for(int i=1;i<=n;i++)
{
while(scanf("%d",&v)&&v)
{
Map[i].push_back(v);
}
}
for(int i=1;i<=n;i++)
{
if(dfn[i]==-1)
{
Tarjan(i);
}
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<Map[i].size();j++)
{
if(pre[i]!=pre[Map[i][j]])
{
CDu[pre[i]]++;
RDu[pre[Map[i][j]]]++;
}
}
}
int ansA=0,ansB=0;
for(int i=0;i<num;i++)
{
if(CDu[i]==0)
{
ansB++;
}
if(RDu[i]==0)
{
ansA++;
}
}
ansB=max(ansA,ansB);
if(num==1)//注意只有一个强连通的时候
{
ansB=0;
}
printf("%d\n%d\n",ansA,ansB);
}
return 0;
}
Kosaraju
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
const int Max = 110;
vector<int>GF[Max];
vector<int>GR[Max];
int path[Max],part[Max];
int RDu[Max],CDu[Max],Num;
bool vis[Max];
int n;
int ansA,ansB;
void Init()
{
for(int i=0;i<=n;i++)
{
GF[i].clear();
GR[i].clear();
}
memset(path,0,sizeof(path));
memset(part,0,sizeof(part));
memset(RDu,0,sizeof(RDu));
memset(CDu,0,sizeof(CDu));
memset(vis,false,sizeof(vis));
Num = 0; ansA = 0; ansB = 0;
}
void DFSF(int u) //正向遍历
{
if(!vis[u])
{
vis[u]=true;
for(int i=0;i<GF[u].size();i++)
{
DFSF(GF[u][i]);
}
path[++path[0]]=u;//这个一定放在遍历完子节点之后
}
}
void DFSR(int u) //反向遍历
{
if(!vis[u])
{
vis[u]=true;
part[u]=part[0];
for(int i=0;i<GR[u].size();i++)
{
DFSR(GR[u][i]);
}
}
}
void Kosaraju()
{
for(int i=1;i<=n;i++)
{
DFSF(i);
}
memset(vis,false,sizeof(vis));
for(int i=n;i>=1;i--)
{
if(!vis[path[i]])
{
++part[0];
DFSR(path[i]);
}
}
for(int i=1;i<=n;i++)//缩点,计算出入度
{
memset(vis,false,sizeof(vis));
for(int j=0;j<GF[i].size();j++)
{
if(part[i]!=part[GF[i][j]]&&!vis[part[GF[i][j]]])
{
vis[part[GF[i][j]]]=true;
CDu[part[i]]++;
RDu[part[GF[i][j]]]++;
}
}
}
for(int i=1;i<=part[0];i++)// 计算答案
{
if(RDu[i]==0)
{
ansA++;
}
if(CDu[i]==0)
{
ansB++;
}
}
ansB = max(ansA,ansB);
if(part[0]==1)
{
ansB = 0;
}
printf("%d\n%d\n",ansA,ansB);
}
int main()
{
while(~scanf("%d",&n))
{
Init();
int v;
for(int i=1;i<=n;i++)
{
while(scanf("%d",&v)&&v)
{
GF[i].push_back(v);
GR[v].push_back(i);
}
}
Kosaraju();
}
return 0;
}