缩点Tarjan算法解析+[题解]受欢迎的牛
(注:我在网上找了一些图,希望原博主不要在意,谢谢,(。☉౪ ⊙。))
首先来了解什么是强连通分量
有向图强连通分量:在有向图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与上一结点连接会形成一个环,环内元素为now与now相连的所有的结点。
实现过程:
从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();
}
}
}
结合这道题再来理解
题目描述
每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果 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算法解析+[题解]受欢迎的牛的更多相关文章
- Tarjan算法模板(USACO03FALL受欢迎的牛)
好文章 #include<bits/stdc++.h> using namespace std; const int N = 10010, M = 50010; int n, m; int ...
- 【tarjan】BZOJ 1051:受欢迎的牛
1051: [HAOI2006]受欢迎的牛 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 3134 Solved: 1642[Submit][Sta ...
- 【强联通分量缩点】【Tarjan】bzoj1051 [HAOI2006]受欢迎的牛
就是看是否有一些点,从其他任何点出发都可到达 定理:有向无环图中唯一出度为0的点,一定可以由任何点出发均可达. 所以缩点,若出度为零的点(强联通分量)唯一,则答案为该强联通分量中点的度数. 若不唯一, ...
- tarjan算法求scc & 缩点
前置知识 图的遍历(dfs) 强连通&强连通分量 对于有向图G中的任意两个顶点u和v存在u->v的一条路径,同时也存在v->u的路径,我们则称这两个顶点强连通.以此类推,强连通分量 ...
- P3387缩点(tarjan+拓扑排序+线性dp)
题目描述 给定一个 n个点 m 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大.你只需要求出这个权值和. 允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次. 输入 ...
- 【BZOJ1051】1051: [HAOI2006]受欢迎的牛 tarjan求强连通分量+缩点
Description 每一头牛的愿望就是变成一头最受欢迎的牛.现在有N头牛,给你M对整数(A,B),表示牛A认为牛B受欢迎. 这种关系是具有传递性的,如果A认为B受欢迎,B认为C受欢迎,那么牛A也认 ...
- Tarjan算法(缩点)
因为最近在学2sat,需要学习前置技能—Tarjan算法,所以花了一天的时间学习这个算法 算法步骤: 1.从一个点开始dfs,并加入栈 2.如果下一个点没有到过,跳到第一步 3.如果下一个点到过,并且 ...
- 洛谷P2341 [HAOI2006]受欢迎的牛 (Tarjan,SCC缩点)
P2341 [HAOI2006]受欢迎的牛|[模板]强连通分量 https://www.luogu.org/problem/P2341 题目描述 每头奶牛都梦想成为牛棚里的明星.被所有奶牛喜欢的奶牛就 ...
- 【模板】缩点(Tarjan算法)/洛谷P3387
题目链接 https://www.luogu.com.cn/problem/P3387 题目大意 给定一个 \(n\) 个点 \(m\) 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之 ...
随机推荐
- xpath教程-通过ID和Class检索 转
通过ID和Class检索 必备知识点 在html中,id是唯一的 在html中,class是可以多处引用的 工具 Python3版本 lxml库[优点是解析快] HTML代码块[从网络中获取或者自 ...
- C++ Primer第5版 第一章课后练习
练习1.9 #include <iostream> int main() { int sum = 0, val = 50; while (val <= 100) { sum += v ...
- zabbix自定义脚本监控服务器端口状态
zabbix可以通过客户端的[net.tcp.port[<ip>,port]]该item监控项来判断本地/远程服务器TCP端口是否正常,不过当时没有想起来,就用了自定义脚本去写的,很久没有 ...
- find for /f 分割字符串 bat
@Echo off::总用例数For /f "tokens=2" %%i in ('Type bat.txt^|Find "Ran"') do (Echo %% ...
- 实在解决不了丢失vs2019之类的msvcr110.dll之类的问题
因为msvcr110.dll也是微软DirectX的一个组件 如果在下载VC运行库没用的情况下,可能是因为要运行的程序是win32的,但是电脑和下载的程序是64的,所以 下载一个win32的即可 如果 ...
- js 小数点失精度
解决方法思路:将小数化成整数后再作运算.具体代码如下: /*** 加法运算,避免数据相加小数点后产生多位数和计算精度损失.** @param num1加数1 | num2加数2*/function ...
- ant-design-vue中tree增删改
ant-design-vue中tree增删改 1. 使用背景 新项目中使用了ant-design-vue组件库.该组件库完全根基数据双向绑定的模式实现.只有表单组件提供少量的方法.所以,在使用ant- ...
- 通过Azure bot framework composer 设计一个AI对话机器人bot(查询天气)
本文介绍通过机器人框架设计器 (Bot framework composer)接近拖拉拽的方式设计一个聊天机器人,该聊天机器人的主要功能是发起http请求查询天气.当然,稍微变通下,可以用来查询几乎任 ...
- [Luogu P1345] [USACO5.4]奶牛的电信Telecowmunication (最小割)
题面 传送门:https://www.luogu.org/problemnew/show/P1345 ] Solution 这道题,需要一个小技巧了解决. 我相信很多像我这样接蒟蒻,看到这道题,不禁兴 ...
- 【Luogu】P1436 棋盘分割 题解
嗯,点开题目,哇!是一道闪亮亮的蓝题! 不要被吓到了,其实,这道题就是一个简单的DP啦! 我们设 \(f[x1][y1][x2][y2][c]\) 为以 \((x1,y1)\) 为左上角,以 \((x ...