POJ 1236Network of Schools
描述
有一些学校连到了一个电脑网络中。这些学校达成了一个协议:每个学校保存一个其他学校名字的列表,列出它发送软件能到达的学校(即接收学校)。注意:如果B在A学校的发送列表中,那么A不一定在B学校的列表中。你需要写一个程序,计算最小的学校数量,这些学校接收新软件后,能让所有的学校在网络内能接收到这个软件(子任务一)。作为一个额外的任务,我们想要保证通过传输新软件到一个任意的学校能使这个软件能在网络中送达到所有的学校。为了达到这个目标,我们可能需要拓展新成员到学校的接收列表。计算这个需要拓展的最小数量,使得无论我们发送新软件到哪个学校,它都可以到达任意一个学校(子任务二)。一个拓展意味着增加一个新成员到某一个学校的接收列表中。
输入:
第一行包括一个整数N:网络中的学校数量(2≤N≤100)。1~N每个数字分别代表一个学校。每个接下来的N行描述了一个接收者列表。第i+1行包括了学校i的接收列表。每个列表以0结束。一个空列表在一行中只有一个单独的0.
输出
你的程序需要写两行作为标准输出。第一行需要包括一个正整数:子任务一的答案。第二行需要包括子任务二的答案。
样例输入
5
2 4 3 0
4 5 0
0
0
1 0
样例输出
1
2
首先 这是一个有向无权图
task1 至少发送软件给几个学校让他们相互流通传递
task2 连线把所有的点相互可达
第一个任务 做强连通分量缩点(相互可达),然后找到入度为零的点,这些点不能接受,只好一开始就送过去了
第二个任务 我讲(搞)不清楚,只知道是缩点后出度为零的点的数量和入度为零的点的数量,谁大就是谁;相等任意;
#include<cstdio>
#include<cstring>
struct node{
int v,first;}no[10010];
struct edge{
int v,next;}e[50010];
int en=0,m;
void addedge(int a,int b){en++;e[en].v=b;e[en].next=no[a].first;no[a].first=en;}//邻接表
int low[10010],st[10010],dfn[10010],index=1,i,j,top=1,vi,in_stack[10010];
//low表示该点所在的强连通子图所在搜索子树的根节点的Dfn值 dfn表示递归中发现该节点的时间 时间则用每递归一次就加一的index表示
int scc[10010],cscc=0,inger[10010],t_in=0,t_out=0,outger[10010];//st 表示栈 top栈顶 in_stack记录一个点是否在栈中
//记录节点在那个强连通分量中 cscc记录强连通分量的个数和作为每一个强连通分量的标记 outger每一个强连通分量的出度 inger每一个强连通分量的入度
void tarjan(int rt)
{low[rt]=dfn[rt]=index++;//刚发现该节点是它自身即为根节点 low等于dfn index加一表时间 in_stack[rt]=1;st[top++]=rt;//入栈 做标记 for(i=no[rt].first;i;i=e[i].next)//遍历从它出去的每一个点 {{vi=e[i].v;if(!dfn[vi]){tarjan(vi);low[rt]=low[rt]<low[vi]?low[rt]:low[vi];}//如果这个点以前还没有发现 就把它递归去 else if(in_stack[vi]==1){low[rt]=low[rt]<dfn[vi]?low[rt]:dfn[vi];}//如果它就在栈里 遇上环了 开心吗? }
// 强连通分量是由若干个环组成的。所以,当有环形成时(也就是搜索的下一个点已在栈中),我们将这一条路径的low值统一,
//即这条路径上的点属于同一个强连通分量。如果遍历完整个搜索树后某个点的dfn值等于low值,
//则它是该搜索子树的根。这时,它以上(包括它自己)一直到栈顶的所有元素组成一个强连通分量。if(low[rt]==dfn[rt])//同上所诉如果low等于dfn 此处应有强连通分量 可能是一个环也可能是一个点 { cscc++;//每记录一个强连通分量 就自行加一 你值得 拥有 do{vi=st[--top];in_stack[vi]=0;scc[vi]=cscc;}while(rt!=vi);//把它们出栈 标记为0,记录同一个强连通分量的标号cscc }
}
int
void out(int n)
{if(cscc==1)printf("1\n0\n");//如果只有一个强连通分量,那还管这么多干什么,直接输出 memset(inger,0,sizeof(inger));memset(outger,0,sizeof(outger));for(i=1;i<=n;i++)//每个点遍历一边 {for(j=no[i].first;j;j=e[j].next)//每个点相连的边遍历一边 if(scc[i]!=scc[e[j].v]){inger[scc[e[j].v]]++;outger[scc[i]]++;}
//如果它们不在同一个强连通分量中 ,化分量为点,记录每个分量的出度 和入度 }for(i=1;i<=cscc;i++){if(inger[i]==0)t_in++;//找到出度为零的强连通分量记录数量 else if(outger[i]==0)t_out++;//找到入度为零的也记下数量 }printf("%d\n",t_in);printf("%d\n",t_in>t_out?t_in:t_out);
}
int main()
{int a,b,n;scanf("%d %d",&n,&m);for(i=1;i<=n;i++)while(scanf("%d",&b)&&b){addedge(b,i);}for(i=1;i<=n;i++)if(!dfn[i])tarjan(i);//把点遍历一边 因为谁也不知道有多少个强连通分量 ,说不定有n个呢 out(n);
}