强连通分量缩点求入度为0的个数和出度为0的分量个数
题目大意:N(2<N<100)各学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输,问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。2,至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。
也就是:
给定一个有向图,求:
1) 至少要选几个顶点,才能做到从这些顶点出发,可以到达全部顶点
2) 至少要加多少条边,才能使得从任何一个顶点出发,都能到达全部顶点
顶点数<= 100
解题思路:
1. 求出所有强连通分量
2. 每个强连通分量缩成一点,则形成一个有向无环图DAG。
3. DAG上面有多少个入度为0的顶点,问题1的答案就是多少
在DAG上要加几条边,才能使得DAG变成强连通的,问题2的答案就是多少
加边的方法:
要为每个入度为0的点添加入边,为每个出度为0的点添加出边
假定有 n 个入度为0的点,m个出度为0的点,如何加边?
把所有入度为0的点编号 0,1,2,3,4 ....N -1
每次为一个编号为i的入度0点可达的出度0点,添加一条出边,连到编号为(i+1)%N 的那个出度0点,
这需要加n条边
若 m <= n,则
加了这n条边后,已经没有入度0点,则问题解决,一共加了n条边
若 m > n,则还有m-n个入度0点,则从这些点以外任取一点,和这些点都连上边,即可,这还需加m-n条边。
所以,max(m,n)就是第二个问题的解
此外:当只有一个强连通分支的时候,就是缩点后只有一个点,虽然入度出度为0的都有一个,但是实际上不需要增加清单的项了,所以答案是1,0;
#include<cstdio> #include<stdlib.h> #include<string.h> #include<string> #include<map> #include<cmath> #include<iostream> #include <queue> //#include <stack> #include<algorithm> #include<set> using namespace std; #define INF 1e8 #define eps 1e-8 #define LL __int64 #define maxn 26 #define mol 1000000007 #define N 1010 #define M 100001 struct Edge { int v; int next; }; Edge edge[M];//边的集合 int node[N];//顶点集合 int instack[N];//标记是否在stack中 int stack[N]; int Belong[N];//各顶点属于哪个强连通分量 int DFN[N];//节点u搜索的序号(时间戳) int LOW[N];//u或u的子树能够追溯到的最早的栈中节点的序号(时间戳) int n, m;//n:点的个数;m:边的条数 int cnt_edge;//边的计数器 int Index;//序号(时间戳) int top; int Bcnt;//有多少个强连通分量 int in[N],out[N],numd[N]; void add_edge(int u, int v)//邻接表存储 { edge[cnt_edge].next = node[u]; edge[cnt_edge].v = v; node[u] = cnt_edge++; } void tarjan(int u) { int i, j; int v; DFN[u] = LOW[u] = ++Index; instack[u] = true; stack[++top] = u; for (i = node[u]; i != -1; i = edge[i].next) { v = edge[i].v; if (!DFN[v])//如果点v没被访问//树枝边 { tarjan(v); if (LOW[v]<LOW[u]) LOW[u] = LOW[v]; } else//如果点v已经被访问过//后向边 if (instack[v] && DFN[v]<LOW[u]) LOW[u] = DFN[v]; } if (DFN[u] == LOW[u])//缩点 { Bcnt++; do { j = stack[top--]; instack[j] = false; Belong[j] = Bcnt; } while (j != u); } } void solve() { int i; top = Bcnt = Index = 0; memset(DFN, 0, sizeof(DFN)); memset(LOW, 0, sizeof(LOW)); memset(Belong, 0, sizeof(Belong)); for (i = 1; i <= n; i++) if (!DFN[i]) tarjan(i); } int main() { //freopen("in.txt", "r", stdin); int i, j, k, t; scanf("%d", &n); cnt_edge = 0; memset(node, -1, sizeof(node)); for (i = 1; i <= n; i++) { while(1) { scanf("%d", &j); if(!j) break; add_edge(i,j); } } solve(); memset(in,0,sizeof(in)); memset(out,0,sizeof(out)); memset(numd,0,sizeof(numd)); for(i=1;i<=n;i++) { for(j=node[i];j!=-1;j=edge[j].next ) { if(Belong[i]!=Belong[edge[j].v]) { in[Belong[edge[j].v]]++; out[Belong[i]]++; } } } int a=0,b=0; for(i=1;i<=Bcnt;i++) { if(!in[i]) a++; if(!out[i]) b++; } if(Bcnt==1) printf("1\n0\n"); else printf("%d\n%d\n",a,max(a,b)); //printf("%d\n",max(a,b)); return 0; }