初涉tarjan缩点

Posted antiquality

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了初涉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
",f[0]);
102     return 0;
103 }

 

 

 

 

END

以上是关于初涉tarjan缩点的主要内容,如果未能解决你的问题,请参考以下文章

tarjan(缩点)

洛谷 P2194 HXY烧情侣Tarjan缩点 分析+题解代码

UVA11504- Dominos(Tarjan+缩点)

tarjan缩点与割点

[BZOJ1179] [Apio2009]Atm(tarjan缩点 + spfa)

Tarjan-缩点