http://poj.org/problem?id=1236
问题概述:n所学校,它们通过单向边连接,如果A-->B表示A学校可以传递信息给B学校,那么问题来了,一:至少
要向几个学校传递信息,才能保证所有学校都能收到信息;二:至少要添加多少组关系,才能保证给任意一个学校
原始信息后,其他所有学校都能收到信息,输入第一个数表示有多少学校,后面n行,第i行第k个数表示i-->k(每行
输入到0结束)(POJ1236)
输入样例: 对应输出:
5 1
2 4 3 0 2
4 5 0
0
0
1 0
两个顶点强连通:有向图G中,两个顶点可以通过某些路径互相到达
强连通图:该有向图中的任意两个顶点都强连通
强连通分量:非强连通有向图中的极大强连通子图(注意不是最大)
定理:
①当一个点既是强连通子图Ⅰ中的点,又是强连通子图Ⅱ中的点,则它是强连通子图Ⅰ∪Ⅱ中的点
②强连通分量一定由若干个环组成的
③在任何深度优先搜索中,同一强连通分量内的所有顶点均在同一棵深度优先搜索树中,也就是说,强连通分量一
定是有向图的某个深搜树子树
Trajan主要原理:
time[k]:第一次遍历到k点的时间
low[k]:k点所在强连通分量子图中第一个被搜到的点的time值
vis[k]:k点是否已经遍历
栈:每当搜索到一个点时,将它压入栈,当这个点k所有子树全部搜索完毕时,如果low[k]==time[k],说明已经找到
了一个强连通分量,将k以及在k之上的元素弹全部出栈(它们全部属于一个强连通分量,且这个强连通分量不含其
它点)
搜索过程:
→当点k有与点c相连,如果此时(time[k]时)c不在栈中,搜索c点,当c点及其子树搜索完毕回溯后,计算出
low[k] = min(low[k], low[c])
→当点k有与点c相连,如果此时(time[k]时)c在栈中,low[k] = min(low[k], time[c])
→搜索完毕后,栈内应该没有点了
期间保证:每个点每条边都只被搜索1次,且必须搜索1次,复杂度n+m,如果遍历完整个搜索树后某个点的time值
等于low值,则它是该搜索子树的根,这时,它以上(包括它自己)一直到栈顶的所有元素组成一个强连通分量,但
属于该强连通分量的所有的点low值不一定一致(但只有根节点满足low值与time值相等)
缩点过程:
→本质:将一个联通块作为一个点ltt[k]:k点属于第ltt[k]个联通块
→遍历一遍所有的边,如果边(u,v)中u和v属于不同联通块,则将它们两个所在的联通块连接在一起
题解:
设ain为缩点之后入度为0的点的个数,aout为缩点之后出度为0的点的个数,显然:这题第一个答案就是ain,第二
个答案当全图强连通时答案为0,否则为max(ain, aout)
#include
#include
#include
#include
using namespace std;
vector v[105], nv[105];
stack st;
int n, t, vis[105], inst[105], low[105], time[105], ltt[105], num, ain, aout, in[105], out[105];
void Trajan(int x);
int main(void)
{
int i, j, x, temp;
scanf("%d", &n);
for(i=1;i<=n;i++)
{
while(scanf("%d", &x), x!=0)
v[i].push_back(x);
}
t = num = 0;
for(i=1;i<=n;i++)
{
if(vis[i]==0)
Trajan(i);
}
for(i=1;i<=n;i++)
{
for(j=0;j