Time Limit: 1000MS Memory Limit: 10000Kmarkdown
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.网络
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.app
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.ide
5
2 4 3 0
4 5 0
0
0
1 0ui
1
2this
IOI 1996spa
题意:学校链接到一个计算机网络,这些学校之间达成一个协议,每个学校维护着一个学校的列表,能够向学校列表中的学校发布软件。
任务A:计算为了使每个学校都能经过网络收到软件,至少须要准备多少份软件拷贝
任务B:要想确保在任意一个学校发放一个新的软件拷贝,全部的学校均可以接受到。必须在列表中增长新的成员,计算须要增长新成员的数目。计算机网络思路:这是一个有向图,则在图中可能存在强连通份量,强连通份量中是相互链接,因此须要缩点,缩点之后会造成一个DAG图,对于一个DAG图咱们只须要在入度为零的点都放上软件就能够使图中的全部点都收到软件,因此ansA为入度为零的点的数目,对于任务B则是将DAG图构成一个强连通份量,最明显的方式就是将出入为零的点与入度为零的点相连,这样就能够使DAG图变成强连通的,而增长的边的数目则要是出度为零点的数目与入度为零点的数目的最大值(本身画画就知道了)code
Tarjanip
#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;
}