(注:我在网上找了一些图,希望原博主不要在意,谢谢,(。☉౪ ⊙。))

首先来了解什么是强连通分量

有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量(strongly connected components)。

——摘自度娘

举个栗子:



在下图中,{1,2,3,4}这4个点中,任意两点之间都可以到达。可以发现只要加入{1,2,3,4}这个集合中加入5,6任意两点,都满足不了任意两点之间都可以到达这个条件,所以不能构成强连通分量。而{5},{6}就只单独算作两个强连通分量。所以这个图可分为3个强连通分量:{1,2,3,4},{5},{6}。

缩点Tarjan算法就是求出所有尽可能大的强连通分量的集合

我们定义几个变量:dfn(时间戳),low(该集合中最早遍历到的点的时间戳),s(正在访问的结点集合,用栈的方式存储),belong(当前结点属于的强连通分量的序数),elem(当前集合中元素的个数),instack(当前结点是否存在正在访问的栈中)。

可以通过定义得到:

low的初始值为该节点的时间戳。

即是:low[now]=dfn[now]

若当前结点now的所连结点next正在被访问,则low[now]=min(low[now],dfn[next])

若当前结点now的所连结点next未被访问,则low[now]=min(low[now],low[next])

可以发现:

low[now]=dfn[now]

则正在访问的结点now与上一结点连接会形成一个环,环内元素为nownow相连的所有的结点。


实现过程:



从1号结点开始搜索,一直搜到6的时候,再也没有路了,此时,low[now]=dfn[now],而栈顶元素就是6,所以{6}就是此图的强连通量之一。




6出栈后,栈顶元素为5,对于5进行上图中6的操作,可发现{5}为一个强连通分量




5出栈,对于3的另一条连边4进行标记,即4进栈




最后对于1的连边2进行标记。回溯后发现:dfn[1]=low[1],从栈顶2到栈的最后一个元素1为一个强连通分量。存储{1,2,3,4}这个强连通分量。


C++实现:

void Tarjan(int now) {
low[now] = dfn[now] = ++tim;
instack[now] = true;
s.push(now);
int SIZ = v[now].size();
for(int i = 0; i < SIZ; i++) {
int next = v[now][i];
if(!dfn[next]) {
Tarjan(next);
low[now] = min(low[now], low[next]);
}
else if(instack[next])
low[now] = min(low[now], dfn[next]);
}
if(dfn[now] == low[now]) {
cnt_set += 1;
int Top = -1;
while(!s.empty() && Top != now) {
elem[cnt_set]++;
Top = s.top();
belong[Top] = cnt_set;
instack[Top] = false;
s.pop();
}
}
}

结合这道题再来理解

题目TP门

题目描述

每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果 A 喜欢 B,B 喜欢C,那么 A 也喜欢 C。牛栏里共有 N 头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。

输入格式

第一行:两个用空格分开的整数:N 和 M。

接下来 M 行:每行两个用空格分开的整数:A 和 B,表示 A 喜欢 B。

输出格式

一行单独一个整数,表示明星奶牛的数量。

输入输出样例

输入

3 3

1 2

2 1

2 3

输出

1

说明/提示

只有 3 号奶牛可以做明星。

思路

首先考虑一个简单的问题:若该图是一个有向无环图,则必有一个点的出度为0。若还存在一个点出度也为0,则本题无解(较为简单就不证了)。

但是,现实是残酷的。

这张图中可能存在环,所以先用Tarjan缩点,将该图转换为有向无环图。在把该图当做上述情况求解即可。

C++代码

#include <stack>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 1e4 + 5;
vector<int> v[MAXN];
stack<int> s;
int dfn[MAXN], low[MAXN], belong[MAXN], elem[MAXN];
bool instack[MAXN];
int tim, cnt_set;
int out[MAXN];
int n, m, ans;
void Read();
void Tarjan(int);
void Build();
void Count();
int main() {
Read();
Build();
Count();
return 0;
}
void Count() {
for(int i = 1; i <= n; i++) {
int SIZ = v[i].size();
for(int j = 0; j < SIZ; j++) {
int next = v[i][j];
if(belong[i] != belong[next])
out[belong[i]]++;
}
}
for(int i = 1; i <= cnt_set; i++) {
if(!out[i]) {
if(!ans)
ans = i;
else {
ans = 0;
break;
}
}
}
printf("%d", elem[ans]);
}
void Build() {
for(int i = 1; i <= n; i++)
if(!dfn[i])
Tarjan(i);
}
void Tarjan(int now) {
low[now] = dfn[now] = ++tim;
instack[now] = true;
s.push(now);
int SIZ = v[now].size();
for(int i = 0; i < SIZ; i++) {
int next = v[now][i];
if(!dfn[next]) {
Tarjan(next);
low[now] = min(low[now], low[next]);
}
else if(instack[next])
low[now] = min(low[now], dfn[next]);
}
if(dfn[now] == low[now]) {
cnt_set += 1;
int Top = -1;
while(!s.empty() && Top != now) {
elem[cnt_set]++;
Top = s.top();
belong[Top] = cnt_set;
instack[Top] = false;
s.pop();
}
}
}
void Read() {
scanf("%d %d", &n, &m);
for(int i = 1; i <= m; i++) {
int A, B;
scanf("%d %d", &A, &B);
v[A].push_back(B);
}
}

缩点Tarjan算法解析+[题解]受欢迎的牛的更多相关文章

  1. Tarjan算法模板(USACO03FALL受欢迎的牛)

    好文章 #include<bits/stdc++.h> using namespace std; const int N = 10010, M = 50010; int n, m; int ...

  2. 【tarjan】BZOJ 1051:受欢迎的牛

    1051: [HAOI2006]受欢迎的牛 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3134  Solved: 1642[Submit][Sta ...

  3. 【强联通分量缩点】【Tarjan】bzoj1051 [HAOI2006]受欢迎的牛

    就是看是否有一些点,从其他任何点出发都可到达 定理:有向无环图中唯一出度为0的点,一定可以由任何点出发均可达. 所以缩点,若出度为零的点(强联通分量)唯一,则答案为该强联通分量中点的度数. 若不唯一, ...

  4. tarjan算法求scc & 缩点

    前置知识 图的遍历(dfs) 强连通&强连通分量 对于有向图G中的任意两个顶点u和v存在u->v的一条路径,同时也存在v->u的路径,我们则称这两个顶点强连通.以此类推,强连通分量 ...

  5. P3387缩点(tarjan+拓扑排序+线性dp)

    题目描述 给定一个 n个点 m 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大.你只需要求出这个权值和. 允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次. 输入 ...

  6. 【BZOJ1051】1051: [HAOI2006]受欢迎的牛 tarjan求强连通分量+缩点

    Description 每一头牛的愿望就是变成一头最受欢迎的牛.现在有N头牛,给你M对整数(A,B),表示牛A认为牛B受欢迎. 这种关系是具有传递性的,如果A认为B受欢迎,B认为C受欢迎,那么牛A也认 ...

  7. Tarjan算法(缩点)

    因为最近在学2sat,需要学习前置技能—Tarjan算法,所以花了一天的时间学习这个算法 算法步骤: 1.从一个点开始dfs,并加入栈 2.如果下一个点没有到过,跳到第一步 3.如果下一个点到过,并且 ...

  8. 洛谷P2341 [HAOI2006]受欢迎的牛 (Tarjan,SCC缩点)

    P2341 [HAOI2006]受欢迎的牛|[模板]强连通分量 https://www.luogu.org/problem/P2341 题目描述 每头奶牛都梦想成为牛棚里的明星.被所有奶牛喜欢的奶牛就 ...

  9. 【模板】缩点(Tarjan算法)/洛谷P3387

    题目链接 https://www.luogu.com.cn/problem/P3387 题目大意 给定一个 \(n\) 个点 \(m\) 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之 ...

随机推荐

  1. 字符集、字符编码、国际化、本地化简要总结(UNICODE/UTF/ASCII/GB2312/GBK/GB18030)

    PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 环境说明   普通的linux 和 普通的windows.    ...

  2. win10 随记

    昨天买的台式电脑,今天到了.有点小激动(用了5年的i3笔记本可以稍微休息下了,哈哈) 拿到电脑,和朋友一块,插线...最终连接成功. 记录下过程中的乌龙操作,,, 1.连接好线路后,显示器没反应,(显 ...

  3. linux中nginx中配置端口转发

    域名指向主机IP地址,通过域名:8080才能访问网站,去掉后面的8080:或者其他的端口号,直接使用域名访问网站 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处 ...

  4. LC滤波器简单设计法 - 一文读懂LC滤波器简单设计方法及原理介绍,LC值计算方法

    LC滤波器概述 LC滤波器也称为无源滤波器,是传统的谐波补偿装置.LC滤波器之所以称为无源滤波器,顾名思义,就是该装置不需要额外提供电源.LC滤波器一般是由滤波电容器.电抗器和电阻器适当组合而成,与谐 ...

  5. presto 查询每天固定时间段

    select task_id,state,createymd,from_unixtime(createtime) "创建时间",manager_name,open_state,ho ...

  6. 【总结】java集合

    一.collection 1.List接口和Set接口都继承自Collection接口,Collection接口继承Iterable接口(Iterable有一个Iterator方法),即可迭代的:Co ...

  7. CF1336 Linova and Kingdom

    题面 给定 n 个节点的有根树,根是 1 号节点. 你可以选择 k 个节点将其设置为工业城市,其余设置为旅游城市. 对于一个工业城市,定义它的幸福值为工业城市到根的路径经过的旅游城市的数量. 你需要求 ...

  8. 从原生web组件到框架组件源码(二)

    innerHTML outerHTML textContent innerText 区别 <div id="test"> <span>sdsdsdsd &l ...

  9. NLP文本多标签分类---HierarchicalAttentionNetwork

    最近一直在做多标签分类任务,学习了一种层次注意力模型,基本结构如下: 简单说,就是两层attention机制,一层基于词,一层基于句. 首先是词层面: 输入采用word2vec形成基本语料向量后,采用 ...

  10. Error: pg_config executable not found.

    pip 安装 psycopg2 安装及错误 现象: Error: pg_config executable not found. Please add the directory containing ...