POJ 1236 强联通


强连通分量缩点求入度为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的都有一个,但是实际上不需要增加清单的项了,所以答案是10


#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;  
}  



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值