POJ 1236-Network of Schools (tarjan求强联通分量模板)

题意:给定你n个点,然后这些点之间会形成一个有向图,然后有两个任务要求。
1:问你最少选择几个节点,使得从这些点出发能发,能够到达所有的顶点。2:问你最少在这个图中加几条边,使得从添加后的图中任意一点出发,能够达到其余的所有顶点。

思路:我们要先用tarjan进行一遍原图的缩点,然后在缩完点后的新图里,对于任务1,我们可以知道,只要找到这个图中的所有入度为0的强联通分量的数目即可。而对于任务二,我们可以知道需要把入度为0的强联通分量和出度为0的强联通分量的最大值。

在这里插入图片描述
我们可以加了这些红色的边之后,这个图就可以满足我们任务二的需求了,所以两个任务就变成了,求解强连通分量中,入度为0和出度为0
的即可。

代码:

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <set>
//tarjan算法 求强联通分量
using namespace std;
typedef long long ll;
const int MAXN = 1e4 + 7;

int head[MAXN],hd[MAXN],cnt,tot,indegree[MAXN],outdegree[MAXN];
struct Edge{
	int next,to;
}edge[MAXN<<1],e[MAXN<<1];
void addedge(int u,int v){ edge[++cnt].to = v;;edge[cnt].next = head[u];head[u] = cnt; }

int dfn[MAXN],low[MAXN];//dfn数组就是时间戳 low数组代表的是当前的点及其后续的点能连接到的深度最浅的点的时间戳为多少
int sta[MAXN],scc[MAXN],time,sccnum,top;//sta维护一个模拟栈来求解scc scc代表当前前点属于哪个强联通分量
//时间复杂度 O(N + V)
void tarjan(int u){
	if(dfn[u]) return;
	dfn[u] = low[u] = ++time;//一开始low数组先指向自己
	sta[++top] = u;
	for(int i = head[u];i;i = edge[i].next){
		int v = edge[i].to;
		if(!dfn[v]){//如果v节点未被访问过
			tarjan(v);
			low[u] = min(low[u],low[v]); 
		}
		else if(!scc[v]){//如果还在栈内
			low[u] = min(low[u],low[v]);
		}
	}
	if(low[u] == dfn[u]){//后代不能找到更浅的点 就以当前点和栈内节点形成了一个强连通分量
		sccnum++;
		while(1){
			int x = sta[top--];
			scc[x] = sccnum;
			if(x == u) break;//强联通分量寻找完成
		}
	}
}

int main()
{
	int n;
	scanf("%d",&n);
	int x;
	for(int i = 1;i <= n;i ++){
		while(~scanf("%d",&x)){
			if(x == 0) break;
			addedge(i,x);
		}
	}
	for(int i = 1;i <= n;i ++){//每一个点 都要tarjan 缩一下点
		if(!dfn[i]) tarjan(i);
	}
	//cout<<"---"<<sccnum<<endl;
	if(sccnum == 1){
		printf("1\n0\n");
		return 0;
	}
	//求缩完点后的新图 属于一个强联通分量的点都缩成了一点
	for(int u = 1;u <= n;u ++){
		for(int i = head[u];i;i = edge[i].next){
			int x = u,y = edge[i].to;
			if(scc[x] != scc[y])
				indegree[scc[y]]++,outdegree[scc[x]]++;
		}
	}
	int ans1 = 0,ans2 = 0,t1 = 0,t2 = 0;
	for(int i = 1;i <= sccnum;i ++){
		if(indegree[i] == 0)
			ans1++,t1++;
		if(outdegree[i] == 0)
			t2++;
	}
	ans2 = max(t1,t2);
	printf("%d\n%d\n",ans1,ans2);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值