Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 3010 | Accepted: 1160 |
Description
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
Output
Sample Input
5 2 4 3 0 4 5 0 0 0 1 0
Sample Output
1 2
Source
各学校之间有单向的网络。如果 A--->B 则如果 A 得到一个软件则可以给学校 B,在这样一个有向图中,有两个问题。
1, 至少要向多少个学校提供软件,可使所有学校都得到软件。
2,至少需要几条边,使得该图变为一个强连通图。
首先求出强连通分支,缩点,之后得到的图,看看这个图有多少个节点的入度为 0,这样的节点数目就是需要提供软件的学校数目,因为不然的话,由于入度为 0,它们会被孤立掉。
第二个问题:设有向图入度,出度为 0 的节点数分别为 Iz,Oz,则通过给它加边,使它变成强连通图,最少要加 max{Iz,Oz} 条边。
【分析】:
第一问很明显是在求最小点基:
下面是一些相关的知识
点基B满足:
对于任意一个顶点Vj,一定存在B中的一个Vi,使得Vi是Vj的前代。
点基包含:顶点数最小的点基 权最小的点基(当所有的权为1时,权最小的点基变成了顶点数的最小的点基)
最高强连通分支:
定义:如果在G中,不存在终点属于[Si]而起点不属于[Si]的弧,就称[Si]为最高强连通分支。
性质:强连通分支Si与图G其他部分相连的所有弧都是向外伸展的。即表明任何非Si中的点与Si中的点没有有向边。说明,最小点基必须在强连通分支中取一个。所以最小点基的个数即为强连通分支的个数。
对于第二问:
一个图是强连通分量的充分条件是图中没有入度为0的点,或出度为0的点,所以我们只要把图中入度和出度为0的点连起来使入度为0的点和出度为0的点消失即可。那么这样最少要连的边就是max(入度为0的点的个数,出度为0的点的个数);
不过需要注意的是:当图被缩成一个点的时候,第一问为1,第二问为0
#include<stdio.h>
#define MAX_SIZE 20008
int stack[MAX_SIZE];
int in[MAX_SIZE],out[MAX_SIZE];//题目中要求记录出度和入度的数组
bool instack[MAX_SIZE];
int N,b_cnt,idx,top;
struct Edge
{
int adj;//adj
Edge* next;//point to next
};
struct Node
{
int DFN,LOW;//定义DFN(u)为节点u搜索的次序编号(时间戳),Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号。
int belongs;//belongs to B_cnt,属于哪一个强联通分支
Edge *first;//point to first,形成链状
} node[MAX_SIZE];
void Init() //initial node[],in[],out[]
{
for(int i=0; i<=N; i++)
{
node[i].first=NULL;
node[i].DFN=node[i].LOW=0;
node[i].belongs=0;
instack[i]=false;
in[i]=out[i]=0;//不在模板中
}
}
void InsertEdge(int u,int v) //insert edge u->v
{
Edge*p=new Edge;
p->adj=v;//指向的下一个节点
p->next=node[u].first;
node[u].first=p;
}
void Tarjan(int u) //Tarjan
{
int v;
node[u].DFN=node[u].LOW=(++idx);
instack[u]=true;
stack[++top]=u;
for(Edge*p=node[u].first; p; p=p->next)
{
v=p->adj;
if(!node[v].DFN)
{
Tarjan(v);
if(node[v].LOW<node[u].LOW)
node[u].LOW=node[v].LOW;
}
else if(instack[v]&&node[v].DFN<node[u].LOW)
node[u].LOW=node[v].DFN;
}
if(node[u].DFN==node[u].LOW)
{
b_cnt++;
do
{
v=stack[top--];
instack[v]=false;
node[v].belongs=b_cnt;
}
while(u!=v);
}
}
void Tarjan_SCC() //StronglyConnectedComponent
{
int i,t1,t2;
b_cnt=idx=top=0;//b_cnt 新图的点数
for(i=1; i<=N; i++)
if(!node[i].DFN)
Tarjan(i);
if(b_cnt<=1)
printf("1/n0/n");
else
{
for(i=1; i<=N; i++)
for(Edge*p=node[i].first; p; p=p->next)
if(node[i].belongs!=node[p->adj].belongs)
in[node[i].belongs]++,out[node[p->adj].belongs]++;
t1=t2=0;
for(i=1; i<=b_cnt; i++)
{
if(!in[i])t1++;
if(!out[i])t2++;
}
printf("%d/n",t2);
printf("%d/n",t1>t2?t1:t2);
}
}
int main()
{
int i,u,v,x;
while(scanf("%d",&N)!=EOF)
{
Init();
for(int i=1;i<=N;i++)
{
while((scanf("%d",&x),x)!=0)
InsertEdge(i,x);
}
Tarjan_SCC();
}
return 0;
}