图论 —— tarjan 缩点 割点 (学习历程)

首先,从模板题开始学起——        

    P3387 【模板】缩点

    思路:

      1. 这道题为什么要 缩点?(什么时候需要缩点)

       根据题目意思,我们只需要找出一条点权最大的路径就行了,不限制点的个数。那么考虑对于一个环上的点被选择了,一整条环是不是应该都被选择,这一定很优,能选干嘛不选。很关键的是题目还允许我们重复经过某条边或者某个点,我们就不需要考虑其他了。因此整个环实际上可以看成一个点(选了其中一个点就应该选其他的点)

——摘录自洛谷第一篇题解

      2. 将强连通分量缩点 ->  建边成为DAG -> DPmax

  

 1 #include<cstdio>
 2 #include<queue>
 3 #include<vector>
 4 #include<cstring>
 5 #include<algorithm>
 6 #define N 100010
 7 #define M 500010
 8 using namespace std;
 9
10 struct node{
11     int from,to,next;
12 }edge[M];
13 queue < int > q;
14 vector < int > cd[N];        //出度
15 vector  < int > rd[N];        //入度
16 int ans[M],t,x,y,v,rds[N],u,n,m,sum,vis[N],d[N],dis[N];
17 int dfn[N],low[N],f[N],time,cnt,k;
18 int stack[N],head[M],visit[N],tot,id;
19
20 void add(int x,int y){        //邻接表加边
21     edge[++cnt].next=head[x];
22     edge[cnt].from=x;
23     edge[cnt].to=y;
24     head[x]=cnt;
25 }
26
27 void tarjan(int x){
28     dfn[x]=low[x]=++time;        //更新时间
29     stack[++id]=x;        //手写栈
30     visit[x]=1;        //入栈
31     for(int i=head[x];i;i=edge[i].next){
32         if(!dfn[edge[i].to]){        //没有更新到的点
33             tarjan(edge[i].to);
34             low[x]=min(low[x],low[edge[i].to]);
35         }
36         else{
37             if(visit[edge[i].to])        //更新过的点
38                 low[x]=min(low[x],dfn[edge[i].to]);
39         }
40     }
41     if(low[x]==dfn[x]){        //注意不在 for 循环内
42         tot++;        //强连通分量编号 (缩点的编号)
43         while(1){
44             vis[stack[id]]=tot;        //vis记录缩点的编号
45             dis[tot]+=d[stack[id]];        //缩点的权值=强连通分量权值累加
46             visit[stack[id]]=0,id--;        //出栈
47             if(x==stack[id+1]) break;        //这个连通块已弹完
48         }
49     }
50 }
51 void topo(){        //拓扑排序
52     for(int i=1;i<=tot;i++) if(rds[i]==0) q.push(i);        //入度数(rds)为 0 ,进队
53     while(!q.empty()){
54         int u=q.front();
55         q.pop();        //队头出队
56         ans[++k]=u;        //ans记录按拓扑序排列的点
57         for(int i=1;i<=cd[u].size();i++){        //cd[u].size(): cd[u][]的长度
58             v=cd[u][i-1];        //因为 vector是从 0开始的,所以减 1
59             rds[v]--;        //入度数--
60             if(rds[v]==0) q.push(v);        //入度数(rds)为 0 ,进队
61         }
62     }
63 }
64
65 int main()
66 {
67     scanf("%d%d",&n,&m);
68     for(int i=1;i<=n;i++) scanf("%d",&d[i]);
69     for(int i=1;i<=m;i++) scanf("%d%d",&x,&y),add(x,y);
70
71     for(int i=1;i<=n;i++)  if(!dfn[i]) tarjan(i);        // tarjan缩点
72
73     for(int i=1;i<=cnt;i++){        //把缩好的点 建边 ->DAG
74         if(vis[edge[i].from]!=vis[edge[i].to]){
75             x=vis[edge[i].from],y=vis[edge[i].to];        // 建 x->y 的边
76             rds[y]++;        // y 的入度数++
77             rd[y].push_back(x);        // 把 x 放入 y 的入度
78             cd[x].push_back(y);        // 把 y 放入 x 的出度
79         }
80     }
81     topo();        // DAG上跑拓扑
82     for(int i=1;i<=tot;i++){        //按照拓扑序(无后效性) 跑 DP
83         int w=ans[i];
84         f[w]=dis[w];
85         for(int j=1;j<=rd[w].size();j++)
86             f[w]=max(f[w],f[rd[w][j-1]]+dis[w]);    //因为 vector是从 0开始的,所以减 1
87     }
88
89     for(int i=1;i<=tot;i++) sum=max(f[i],sum);        //最后统计答案
90     printf("%d",sum);
91     return 0;
92 }

    P3388 【模板】割点(割顶)

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define N 100010
 4 using namespace std;
 5 struct node{
 6     int next,to;
 7 }edge[N*2];
 8 int n,m,dfn[N],cut[N],head[N],low[N],tot,cnt,id;
 9
10 void add(int x,int y){
11     edge[++cnt].next=head[x];
12     edge[cnt].to=y;
13     head[x]=cnt;
14 }
15
16 void  tarjan(int x,int root){        //割点
17     int child=0;
18     dfn[x]=low[x]=++id;
19     for(int v,i=head[x];i;i=edge[i].next){
20         v=edge[i].to;
21         if(!dfn[v]){        //没有更新过的
22             tarjan(v,root);
23             low[x]=min(low[x],low[v]);
24             if(low[v]>=dfn[x]&&x!=root) cut[x]=1;    //x不为根的割点判断条件: low[v]>=dfn[x]
25             if(x==root) child++;        //若 x为根
26         }
27         else if(x!=root) low[x]=min(low[x],dfn[v]);
28     }
29     if(child>=2&&x==root) cut[x]=1;        //x为根的割点判断条件:有2棵及以上的子树
30 }
31 int main()
32 {
33     scanf("%d%d",&n,&m);
34     for(int u,v,i=1;i<=m;i++){
35         scanf("%d%d",&u,&v);
36         add(u,v),add(v,u);
37     }
38     for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,i);
39     for(int i=1;i<=n;i++) if(cut[i]) tot++;
40     printf("%d\n",tot);
41     for(int i=1;i<=n;i++) if(cut[i]) printf("%d ",i);
42     return 0;
43 }

原文地址:https://www.cnblogs.com/RR-Jin/p/11617370.html

时间: 2024-11-12 03:11:26

图论 —— tarjan 缩点 割点 (学习历程)的相关文章

tarjan缩点与割点

Tarjan算法 先是废话时间:说来挺惭愧 , 好几个月以前就学过tarjan算法然而现在才第一次写 模板题:[luogu P3387][模板]缩点 tarjan缩点&dp 为啥要缩点答案显然 把环缩成一个点 然后图上拓扑dp tarjan同名算法有很多 , 比如本blog的缩点与割点的tarjan算法其实并不是一个东西 , 但是很是相似 这个tarjan , 需要三个东西 第一:一个栈来存放搜到的点 第二:一个时间戳dfn , 表示第几个搜到这个点的 第三:low数组 , 表示够追溯到的最早的

POJ 3352 Road Construction(图论-tarjan)

Road Construction Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 8647   Accepted: 4318 Description It's almost summer time, and that means that it's almost summer construction time! This year, the good people who are in charge of the ro

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

洛谷 P2194 HXY烧情侣[Tarjan缩点] 分析+题解代码 题目描述: 众所周知,HXY已经加入了FFF团.现在她要开始喜(sang)闻(xin)乐(bing)见(kuang)地烧情侣了.这里有n座电影院,n对情侣分别在每座电影院里,然后电影院里都有汽油,但是要使用它需要一定的费用.m条单向通道连接相邻的两对情侣所在电影院.然后HXY有个绝技,如果她能从一个点开始烧,最后回到这个点,那么烧这条回路上的情侣的费用只需要该点的汽油费即可.并且每对情侣只需烧一遍,电影院可以重复去.然后她想花尽

大三仍是Linux系统小白的我给大家讲讲学习历程

我与Linux结缘是在大三的时候.我与Linux熟识是在偶然遇到<Linux就该这么学>的时候.因为我是电子信息工程专业,在高年级时开设了嵌入式课程,嵌入式系统是一种专用的计算机系统,作为装置或设备的一部分.所有带有数字接口的设备,如录像机.车子等,都使用嵌入式系统,有些嵌入式系统还包含操作系统.嵌入式操作系统包括μC/OS-II.嵌入式Linux.VxWorks等,但大部分嵌入式操作系统是不开源且不能免费使用,只有Linux是基于GPL协议,所以它成为了嵌入式系统的绝对主流. 我们学校的嵌入

【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国想找出一个路径切断方案

SOC学习历程概述

从开始接触soc到现在大概有两年半左右的时间了,经历了ORSOC到minsoc再到mkg-soc的搭建,以及现在的大小核系统的搭建 首先先讲下学习的前期需要具备的知识,前面3点是必须,后面3点可以中间学习的过程再学习.之所以有这些要求主要是以防中间的学习过程中,有些东西看不懂而走弯路. 学习的前期准备:1.学过数电,有一定的电路基础.2.熟练掌握verilog语言.3.对于计算机组成原理,体系结构有一定的了解.4.学过单片机编程,写过一些简单的裸机程序,最好能够玩过microblaze这样对于软

[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的

HDU 3207 Ikki&#39;s Story IV - Panda&#39;s Trick(图论-2SAT,图论-tarjan)

Ikki's Story IV - Panda's Trick Time Limit: 1000MS   Memory Limit: 131072K Total Submissions: 7821   Accepted: 2892 Description liympanda, one of Ikki's friend, likes playing games with Ikki. Today after minesweeping with Ikki and winning so many tim