初涉tarjan缩点

tarjan缩点:口胡过好多题,不过从来没写过……

什么是缩点

tarjan和Kosaraju、Gabow算法一样,是为了求有向图中的强连通分量。因为有向图中大多数情况下会有环存在,而有环是一个不甚好的性质。如果把有向图里的所有强连通分量都看作是一个点(缩点),则原图就会变成一个DAG——DAG是一个好东西。

什么是tarjan缩点

tarjan算法网上大多有介绍,我也在之前看过多次,不过从未写过,这里不再介绍。

今天把核心代码重新看了一遍,终于深入理解了其算法。那么就不妨在这里直接放上代码。

tarjan代码

 1     void tarjan(int now)
 2     {
 3         dfn[now] = low[now] = ++tim;    //常规的dfn[]和low[]
 4         stk[++cnt] = now;
 5         for (int i=head[now]; i!=-1; i=nxt[i])
 6         {
 7             int v = edges[i];
 8             if (!dfn[v]){
 9                 tarjan(v);
10                 low[now] = std::min(low[now], low[v]);
11             }else if (!col[v])
12                 low[now] = std::min(low[now], dfn[v]);  //注意这里是dfn[v]
13         }
14         if (low[now]==dfn[now])  //最后的统计部分
15         {
16             col[now] = ++cols;
17             for (; stk[cnt]!=now; cnt--)
18                 col[stk[cnt]] = cols;
19             cnt--;
20         }
21     } 

用途

1.有向图的缩点

2.解决2-SAT

几道例题

P3387 【模板】缩点

题目背景

缩点+DP

题目描述

给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。

允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。

输入输出格式

输入格式:

第一行,n,m

第二行,n个整数,依次代表点权

第三至m+2行,每行两个整数u,v,表示u->v有一条有向边

输出格式:

共一行,最大的点权之和。

说明

n<=10^4,m<=10^5,点权<=1000

算法:Tarjan缩点+DAGdp


题目分析

嘛……具体算法在题面里都给出来了。缩点+dp。

我做法是缩完点后重新建边,长是长了点,不过要是到了其他题的话可移植性会大一些。

至于dp,只需要建一个超级源,然后从超级源开始记忆化搜索就好了。

  1 #include<bits/stdc++.h>
  2 const int maxn = 10005;
  3 const int maxm = 100035;
  4
  5 int n,m;
  6 int f[maxn],val[maxn],col[maxn],cols;
  7 int edgeTot,edges[maxm],nxt[maxm],head[maxn];
  8 bool vis[maxn];
  9
 10 int read()
 11 {
 12     char ch = getchar();
 13     int num = 0;
 14     bool fl = 0;
 15     for (; !isdigit(ch); ch = getchar())
 16         if (ch==‘-‘) fl = 1;
 17     for (; isdigit(ch); ch = getchar())
 18         num = (num<<1)+(num<<3)+ch-48;
 19     if (fl) num = -num;
 20     return num;
 21 }
 22 namespace tarjanSpace
 23 {
 24     int stk[maxn],cnt;
 25     int a[maxn],dfn[maxn],low[maxn],tim;
 26     int edgeTot,edges[maxm],nxt[maxm],head[maxn];
 27     void tarjan(int now)
 28     {
 29         dfn[now] = low[now] = ++tim;
 30         stk[++cnt] = now;
 31         for (int i=head[now]; i!=-1; i=nxt[i])
 32         {
 33             int v = edges[i];
 34             if (!dfn[v]){
 35                 tarjan(v);
 36                 low[now] = std::min(low[now], low[v]);
 37             }else if (!col[v])
 38                 low[now] = std::min(low[now], dfn[v]);
 39         }
 40         if (low[now]==dfn[now])
 41         {
 42             ::col[now] = ++::cols;
 43             for (; stk[cnt]!=now; cnt--)
 44                 ::col[stk[cnt]] = ::cols;
 45             cnt--;
 46         }
 47     }
 48     inline void addedgeInner(int u, int v)
 49     {
 50         edges[++edgeTot] = v, nxt[edgeTot] = head[u], head[u] = edgeTot;
 51     }
 52     inline void addedgeOuter(int u, int v)
 53     {
 54         ::edges[++::edgeTot] = v, ::nxt[::edgeTot] = ::head[u], ::head[u] = ::edgeTot;
 55     }
 56     void dealOuter()
 57     {
 58         for (int i=1; i<=n; i++) ::val[::col[i]] += a[i];
 59         for (int i=1; i<=n; i++)
 60         {
 61             int u = col[i];
 62             for (int j=head[i]; j!=-1; j=nxt[j])
 63             {
 64                 int v = col[edges[j]];
 65                 addedgeOuter(u, v);
 66             }
 67         }
 68         for (int i=1; i<cols; i++) addedgeOuter(0, i);
 69     }
 70     void solve()
 71     {
 72         memset(head, -1, sizeof head);
 73         n = read(), m = read(), cnt = tim = edgeTot = 0;
 74         for (int i=1; i<=n; i++) a[i] = read(), addedgeInner(0, i);
 75         for (int i=1; i<=m; i++)
 76         {
 77             int u = read(), v = read();
 78             addedgeInner(u, v);
 79         }
 80         tarjan(0);
 81         dealOuter();
 82     }
 83 }
 84 void dp(int now)
 85 {
 86     if (vis[now]) return;
 87     vis[now] = 1;
 88     for (int i=head[now]; i!=-1; i=nxt[i])
 89     {
 90         int v = edges[i];
 91         dp(v);
 92         f[now] = std::max(f[now], f[v]);
 93     }
 94     f[now] += val[now];
 95 }
 96 int main()
 97 {
 98     memset(head, -1, sizeof head);
 99     tarjanSpace::solve();
100     dp(0);
101     printf("%d\n",f[0]);
102     return 0;
103 }

END

原文地址:https://www.cnblogs.com/antiquality/p/9260534.html

时间: 2024-10-14 21:45:58

初涉tarjan缩点的相关文章

【BZOJ-1924】所驼门王的宝藏 Tarjan缩点(+拓扑排序) + 拓扑图DP

1924: [Sdoi2010]所驼门王的宝藏 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 787  Solved: 318[Submit][Status][Discuss] Description Input 第一行给出三个正整数 N, R, C. 以下 N 行,每行给出一扇传送门的信息,包含三个正整数xi, yi, Ti,表示该传送门设在位于第 xi行第yi列的藏宝宫室,类型为 Ti.Ti是一个1~3间的整数, 1表示可以传送到第 xi行任意

【BZOJ-1797】Mincut 最小割 最大流 + Tarjan + 缩点

1797: [Ahoi2009]Mincut 最小割 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1685  Solved: 724[Submit][Status][Discuss] Description A,B两个国家正在交战,其中A国的物资运输网中有N个中转站,M条单向道路.设其中第i (1≤i≤M)条道路连接了vi,ui两个中转站,那么中转站vi可以通过该道路到达ui中转站,如果切断这条道路,需要代价ci.现在B国想找出一个路径切断方案

[BZOJ 1051][HAOI 2006]受欢迎的牛(tarjan缩点)

http://www.lydsy.com:808/JudgeOnline/problem.php?id=1051 唔...这题好像在POJ上见过? 比较水的题,很好想出思路.牛和牛之间的关系就像有向图,牛a喜欢牛b相当于建立有向边a->b,然后在这个有向图中,每个强连通分量里的牛们相当于是相互喜欢的,把这个图缩点成DAG,DAG里如果有且仅有一个出度为0的点,则这个点对应强连通分量里的所有牛都是受欢迎的牛,如果没有出度为0的点,当然就没受欢迎的牛了,如果出度为0的点的个数大于1,则每个出度为0的

tarjan缩点以及链式前向星的基本+应用(洛谷1262 间谍网络)

题目描述 由于外国间谍的大量渗入,国家安全正处于高度的危机之中.如果A间谍手中掌握着关于B间谍的犯罪证据,则称A可以揭发B.有些间谍收受贿赂,只要给他们一定数量的美元,他们就愿意交出手中掌握的全部情报.所以,如果我们能够收买一些间谍的话,我们就可能控制间谍网中的每一分子.因为一旦我们逮捕了一个间谍,他手中掌握的情报都将归我们所有,这样就有可能逮捕新的间谍,掌握新的情报. 我们的反间谍机关提供了一份资料,色括所有已知的受贿的间谍,以及他们愿意收受的具体数额.同时我们还知道哪些间谍手中具体掌握了哪些

【BZOJ-2438】杀人游戏 Tarjan + 缩点 + 概率

2438: [中山市选2011]杀人游戏 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1638  Solved: 433[Submit][Status][Discuss] Description 一位冷血的杀手潜入 Na-wiat,并假装成平民.警察希望能在 N 个人里面,查出谁是杀手. 警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他认识的人, 谁是杀手, 谁是平民. 假如查证的对象是杀手, 杀手将会把警察干掉. 现在警察掌

UVA11504- Dominos(Tarjan+缩点)

题目链接 题意:多米诺骨牌的游戏,给出一些牌,以及哪张牌倒了之后会推倒哪张牌,求最少的推倒牌的张数,使得所有牌都倒下去. 思路:有向图的强连通分量,用Tarjan缩点之后找出入度为0的点的个数,即为答案. 代码: #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAXN = 100100;

UVA 11324.The Largest Clique tarjan缩点+拓扑dp

题目链接:https://vjudge.net/problem/UVA-11324 题意:求一个有向图中结点数最大的结点集,使得该结点集中任意两个结点u和v满足:要目u可以到达v,要么v可以到达u(相互可达也可以). 思路:同一个强联通分量中满足结点集中任意两个结点u和v满足:要目u可以到达v,要么v可以到达u(相互可达也可以).把强联通分量收缩点后得到scc图,让每个scc结点的权值等于他的结点数,则求scc图上权最大的路径.拓扑dp,也可以直接bfs,但是要建立一个新的起点,连接所有入度为0

UvaLive4287 roving Equivalences(Tarjan缩点+DAG)

UvaLive4287 roving Equivalences 题意:给n个定理,以及m个关系,即u定理可以推出v定理.问至少还需要加多少个条件,才能是定理两两互推. 思路:Tarjan缩点.然后变成一个DAG.ans1记录入度为0的联通块,ans2记录出度为0的联通块.输出较大值即可.注意如果点数为1或者只有一个强连通分量要输出0. /* ID: onlyazh1 LANG: C++ TASK: Knights of the Round Table */ #include<iostream>

强连通分量tarjan缩点——POJ2186 Popular Cows

这里的Tarjan是基于DFS,用于求有向图的强联通分量. 运用了一个点dfn时间戳和low的关系巧妙地判断出一个强联通分量,从而实现一次DFS即可求出所有的强联通分量. §有向图中, u可达v不一定意味着v可达u.    相互可达则属于同一个强连通分量    (Strongly Connected Component, SCC) §有向图和它的转置的强连通分量相同 §所有SCC构成一个DAG(有向无环图) dfn[u]为节点u搜索的次序编号(时间戳),即首次访问u的时间 low[u]为u或u的