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<stdio.h>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
vector<int> v[105], nv[105];
stack<int> 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<v[i].size();j++)
{
temp = v[i][j];
if(ltt[i]!=ltt[temp])
nv[ltt[i]].push_back(ltt[temp]);
}
}
for(i=1;i<=num;i++)
{
for(j=0;j<nv[i].size();j++)
in[nv[i][j]]++, out[i]++;
}
ain = aout = 0;
for(i=1;i<=num;i++)
{
if(in[i]==0)
ain++;
if(out[i]==0)
aout++;
}
printf("%d\n", ain);
if(num==1)
printf("0\n");
else
printf("%d\n", max(ain, aout));
return 0;
}
void Trajan(int x)
{
int i, temp;
st.push(x);
inst[x] = vis[x] = 1;
low[x] = time[x] = ++t;
for(i=0;i<v[x].size();i++)
{
temp = v[x][i];
if(vis[temp]==0)
{
Trajan(temp);
low[x] = min(low[x], low[temp]);
}
else if(inst[temp]==1)
low[x] = min(low[x], time[temp]);
}
if(low[x]==time[x])
{
num++;
while(st.empty()==0)
{
temp = st.top();
st.pop();
ltt[temp] = num;
inst[temp] = 0;
if(temp==x)
break;
}
}
}