缩点(有向图的强连通分量)学习笔记

缩点(有向图的强连通分量)学习笔记

1.什么是强连通分量?:

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

显然正确,但是文绉绉的又难懂

其实就是有一大团点,它们之间可以互相到达,且没有其它的点与它们互可到达,那么这一大团点就是有向图的一个强联通分量。

一个强连通分量可以看作一个或几个环拼接在一起。

如:

2.如何操作?:

首先对于整个有向图进行dfs,若dfs树中存在子树有到达父亲节点,从父亲到这个点则都可互相到达,这些点有可能与父亲原属的强连通图合并,也有可能成为新的一个强连通分量。

如上图。

所以我们判断一个点是否在一个强连通分量中,就用它是否能到达dfs序比他小的点。

但我们如何将一个强连通分量的点记录呢?

答:用一个栈储存,遇到一个不能到达dfs序比他小的点的点,就将栈里的点都标记为一个新的强连通分量,再清空栈(一个点我们也看作一个强连通分量)。

我们用dfn数组记录dfs序,low数组记录能到达的点最小的dfs编号。

对于每一个点,需要用它的儿子的low来更新自己的low,如上图中的2号点;

然而,若有连向父亲的边呢?是不是直接用父亲的dfn来更新自己的low?

当然是对的!!!(作者就曾经错在这里,曾今以为是对的,但打模板时错了,改了两处,结果以为是因为将dfn改为low才对的,后面发现不是,果然我还是太蒟蒻了qaq~~~~~

在tarjan算法中,都是用父亲的dfn来更新自己的low,

但在有向图中,若自己的父亲有连向祖父等祖宗,那么自己和祖宗在同一个强连通分量;

而在无向图中,则属于两个强连通分量。

如:

                                 

其中,1、2、3、4、5互可到达,只有dfn[1]==low[1],都是一个强连通分量。

           

此时,用low来更新错误

 

还有,注意:对于已经属于一个强连通分量的点,不能用它的low来更新连向它的点

       

所以,low[5]为5。

 

 1 void tarjan(int u)//当前点
 2 {
 3     low[u]=dfn[u]=++deep; x[++top]=u; v[u]=1;
 4     for(int i=head1[u];i;i=e[i].nxt)
 5     {
 6         if(!dfn[e[i].to]) tarjan(e[i].to),low[u]=min(low[u],low[e[i].to]);//连向儿子的边
 7         else if(v[e[i].to]) low[u]=min(low[u],low[e[i].to]);//连向父亲且不属于一个强连通分量
 8     }
 9    if(dfn[u]==low[u])//找到一个强连通分量
10    {
11         num[u]=++tot; v[u]=0;
12         while(x[top]!=u) v[x[top]]=0,num[x[top]]=tot,--top;
13         --top;
14      }
15 }
16 int main()
17 {
18     for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
19     return 0;
20 }

 

 

 

最后,模拟一个图的tarjan过程:

 

一共有一个强连通分量:1.{1,2,3,4,5,6,7,8,9}。

u=1:dfn[1]=1,++top,x[1]=1,tarjan(8);

u=8:dfn[8]=2,++top,x[2]=8,tarjan(9);

u=9:dfn[9]=3,++top,x[3]=9,low[9]=1;

u=8:low[8]=1;

u=1:tarjan(2);

u=2:dfn[2]=4,++top,x[4]=2,tarjan(3);

u=3:dfn[3]=5,++top,x[5]=3,tarjan(4);

u=4:dfn[4]=6,++top,x[6]=4,low[4]=low[2]=4;

u=3:low[3]=low[4]=4;

u=2:low[2]=4,tarjan(5);

u=5:dfn[5]=7,++top,x[7]=5,tarjan(6);

u=6:dfn[6]=8,++top,x[8]=6,tarjan(7);

u=7:dfn[7]=9,++top,x[9]=7,low[7]=1;

u=6:low[6]=1;

u=5:low[5]=1;

u=2:low[2]=1;

u=1:dfn[1]==low[1]=1→++tot,将x[1]到x[9]标记为tot=1,top=0;

结束。

3.缩点:

将每个强联通分量看作一点,将所有连向强联通分量内的点连向这个强联通分量。

 1 int main()
 2 {
 3     n=read(); m=read();
 4     for(int i=1;i<=n;++i) w1[i]=read();
 5     for(re int i=1;i<=m;++i) t1=read(),t2=read(),add(t1,t2,e,head1);
 6     cnt=0;
 7     for(int i=1;i<=n;++i)
 8     {
 9         w2[num[i]]+=w1[i];
10         for(int j=head1[i];j;j=e[j].nxt) if(num[i]!=num[e[j].to]) add(num[i],num[e[j].to],f,head2),++rd[num[e[j].to]];
11     }
12     return 0;
13 }

洛谷P3387 【模板】缩点

题目背景

缩点+DP

题目描述

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

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

输入输出格式

输入格式:

 

第一行,n,m

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

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

 

输出格式:

 

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

 

输入输出样例

输入样例#1: 
2 2
1 1
1 2
2 1
输出样例#1: 
2

说明

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

算法:Tarjan缩点+DAGdp

题解:

当进入一个强连通分量时,肯定将这个强连通分量都走一遍,所以缩点,变为一个DAG(有向无环图),然后按拓扑序dp,每个点的答案为所有能到达它的点的答案的最大值+自己的点值,输出所有点中答案的最大值。

用每个点的答案更新它所能到达的所有点的答案。

代码如下:

 1 #include<bits/stdc++.h>
 2 #define re register
 3 using namespace std;
 4 const int N=10006,M=100006;
 5 int n,m,t1,t2,w1[N],w2[N],num[N],cnt=0,head1[N],t,tot=0,head2[N],x[N],dfn[N],low[N],ans[N],maxa=-1,rd[N],deep=0,top=0,v[N];
 6 struct edge
 7 {
 8     int to,nxt;
 9 }e[M],f[M];
10 inline void add(int u,int v,edge a[],int head[]){a[++cnt].to=v,a[cnt].nxt=head[u],head[u]=cnt;}
11 inline int read()
12 {
13     int T=0,F=1; char ch=getchar();
14     while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();}
15     while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar();
16     return F*T;
17 }
18 void tarjan(int u)
19 {
20     low[u]=dfn[u]=++deep; x[++top]=u; v[u]=1;
21     for(int i=head1[u];i;i=e[i].nxt)
22     {
23         if(!dfn[e[i].to]) tarjan(e[i].to),low[u]=min(low[u],low[e[i].to]);
24         else if(v[e[i].to]) low[u]=min(low[u],low[e[i].to]);
25     }
26     if(dfn[u]==low[u])
27     {
28         num[u]=++tot; v[u]=0;
29         while(x[top]!=u) v[x[top]]=0,num[x[top]]=tot,--top;
30         --top;
31     }
32 }
33 void tppx()
34 {
35     queue<int> q; cnt=0;
36     for(int i=1;i<=tot;++i) if(!rd[i]) x[++cnt]=i,q.push(i);
37     while(!q.empty())
38     {
39         int tmp=q.front(); q.pop();
40         for(int i=head2[tmp];i;i=f[i].nxt)
41         {
42             --rd[f[i].to];
43             if(!rd[f[i].to]) x[++cnt]=f[i].to,q.push(f[i].to);
44         }
45     }
46 }
47 int main()
48 {
49     n=read(); m=read();
50     for(int i=1;i<=n;++i) w1[i]=read();
51     for(re int i=1;i<=m;++i) t1=read(),t2=read(),add(t1,t2,e,head1);
52     for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
53     cnt=0;
54     for(int i=1;i<=n;++i)
55     {
56         w2[num[i]]+=w1[i];
57         for(int j=head1[i];j;j=e[j].nxt) if(num[i]!=num[e[j].to]) add(num[i],num[e[j].to],f,head2),++rd[num[e[j].to]];
58     }
59     memset(x,0,sizeof(x)); tppx();
60     for(int i=1;i<=n;++i)
61     {
62         t=x[i]; ans[t]=max(ans[t],w2[t]);
63         maxa=max(maxa,ans[t]);
64         for(int j=head2[t];j;j=f[j].nxt) ans[f[j].to]=max(ans[f[j].to],ans[t]+w2[f[j].to]);
65     }
66     printf("%d\n",maxa);
67     return 0;
68 }

 

    

转载于:https://www.cnblogs.com/ljk123-de-bo-ke/p/10686498.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/279712.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql多表联合删除

文件一&#xff1a;01.txt文件二&#xff1a;02.txt登录mysql选择数据库表user结构表user_depart结构导入数据到表user导入数据到表user_depart联合删除查看删除后user表的数据查看删除后user_depart表的数据本文转自 Lee_吉 51CTO博客&#xff0c;原文链接:http://blog.51cto.…

Jenkins 随笔

window是 随笔 修改端口 &#xff1a; <arguments>-Xrs -Xmx256m -Dhudson.lifecyclehudson.lifecycle.WindowsServiceLifecycle -jar "%BASE%\jenkins.war" --httpPort8181 --webroot"%BASE%\war"</arguments> 然后重启服务&#xff08;服务…

centos 初学者_初学者:如何在Outlook 2013中创建和管理任务

centos 初学者If you’re one of those people who has a whiteboard or notepad with an ever-evolving to-do list, or your desk and monitors are adorned with Post-its reminding you of important events, then this the article for you. 如果您是拥有不断发展的待办事…

C语言基础(五)

一、字符串相关函数 1.gets()(输入字符串) 格式&#xff1a;gets(字符串)&#xff1b; (1)区别&#xff1a;gets(str)与scanf(“%s”,str) gets(str)允许输入的字符串含有空格 scanf(“%s”,str)不允许含有空格 注意&#xff1a;由于以上无法知道字符串大小&#xff0c;很容易导…

新服务器安装和配置zabbix的playbook

公司的金山区云服务器是由我负责的&#xff0c;每一次新购买了金山区的服务器都要把这些新服务器添加到zabbix监控里&#xff0c;于是我就编写了一个ansible的playbook&#xff0c;这样以后就可以在执行playbook的时候“带薪拉屎”了。 ansible主机准备&#xff1a; 1&#xff…

15个变态的Google面试题以及答案

在当前经济形势不景气的情况下&#xff0c;谷歌招聘新员工是一件令人振奋的事&#xff0c;特别是对那些在当前金融风暴中渴望找到安全港的年轻经理们和软件开发商们来说是个好消息。   不过&#xff0c;也不要高兴太早&#xff0c;谷歌在招聘新员工时&#xff0c;更加青睐名牌…

小程序禁用ios 左右滑动_如何在使用应用程序时禁用iOS控制中心

小程序禁用ios 左右滑动The Control Center has proven to be a thoughtful and welcome addition to iOS, but it can be annoying sometimes if you’re playing a game or using an app, and you accidentally open it. Here’s how you can disable it in such situations.…

repomd.xml错误14 not found

用Centos7最小化安装了系统&#xff0c;想练练手&#xff0c;可以到换了“搜狐”的YUM源&#xff0c;系统总报错更新错误说找不到repomd.xml。 然后就一直搜解决问题&#xff0c;能用到的都用到了&#xff0c;网上提到的都用到了。浪费了好几个小时没解决。正当无语的时候&…

浅谈javascript递归(白话版)

递归 递归是一种解决问题的方法&#xff0c;通常我们可以理解成函数调用自身&#xff1b; 什么递归&#xff1f;递归怎么写&#xff1f; 首先直接调用自身的方法和函数&#xff0c;他是一个递归&#xff0c;我们看代码&#xff1a; 复制代码 var recursiveFun function(params…

超链接禁用_如何在Microsoft Word中禁用超链接

超链接禁用When you type a web or email address in Word, you may notice that the program automatically formats it as a live hyperlink. This is a setting in Word’s AutoFormat feature that is on by default but can be easily turned off. 当您在Word中键入网站或…

ssh面试题总结

题目1&#xff1a;Hibernate工作原理及为什么要用&#xff1f; 原理&#xff1a; hibernate&#xff0c;通过对jdbc进行封装&#xff0c;对 java类和 关系数据库进行mapping&#xff0c;实现了对关系数据库的面向对象方式的操作&#xff0c;改变了传统的jdbc sql操作数据的方式…

SaltStack的salt-ssh使用及LAMP状态设计部署

SaltStack的salt-ssh使用及LAMP状态设计部署 1、salt-ssh的使用 官方文档&#xff1a;https://docs.saltstack.com/en/2016.11/topics/ssh/index.html &#xff08;1&#xff09;安装salt-ssh [rootlinux-node1 ~]# yum install -y salt-ssh&#xff08;2&#xff09;配置salt-…

程序员笔记(知识)管理的一点经验

记笔记这件事&#xff0c;也许在很多人看来&#xff0c;再普通、简单不过了——从小老师就教育我们要这么做。不同的人有不同的方式&#xff0c;我们最终的目的&#xff0c;还是希望不要停留在只是记录这一层面上&#xff0c;而是将它们转变为我们的知识。作为一个程序员&#…

xbox可以录视频声音吗_什么是Xbox Live Gold,它值得吗?

xbox可以录视频声音吗If you have an Xbox One or Xbox 360, Microsoft’s Xbox Live Gold service is required to play multiplayer games online. A subscription costs $10 per month or $60 per year. Xbox Live Gold also includes additional benefits, like free games…

windows - mysql

Windows:(mysql)操作:0.下载安装mysql www.mysql.org downloads community 5.7.21 下载5.6 Microsoft Windows 解压到C: C:\mysql-5.6.39-winx64 C:\mysql-5.6.39-winx64\bin bin/mysql 客户端 bin/mysqld 服务端 设置环境变量: …

显示器选三星还是飞利浦_如何为飞利浦色相灯设置计时器

显示器选三星还是飞利浦Maybe you want to turn off your Philips Hue lights after a certain amount of time has passed, or have them blink as a reminder. Whatever your needs, here’s how to set a timer for your Philips Hue lights to have them automatically tur…

PIE SDK与OpenCV结合说明文档

1.功能简介 OpenCV是基于BSD许可&#xff08;开源&#xff09;发行的跨平台计算机视觉库&#xff0c;可以运行在Linux、Windows、Android和Mac OS操作系统上。它轻量级而且高效——由一系列 C 函数和少量 C 类构成&#xff0c;同时提供了Python、Ruby、MATLAB等语言的接口&…

js的栈堆与浅拷贝、深拷贝的理解

一&#xff1a;什么是堆栈&#xff1f; 我们都知道&#xff1a;在计算机领域中&#xff0c;堆栈是两种数据结构&#xff0c;它们只能在一端(称为栈顶(top))对数据项进行插入和删除。 堆&#xff1a;队列优先,先进先出&#xff1b;由操作系统自动分配释放 &#xff0c;存放函数的…

python面向对象基础语言进阶

在此感谢前辈们的指导&#xff1a;http://python.jobbole.com/80955/ https://www.cnblogs.com/wupeiqi/p/4766801.htmlhttps://www.cnblogs.com/paomaliuju/p/5122761.html https://www.cnblogs.com/goser/articles/7097728.html http://www.cnblogs.com/alex3714/articles/52…

ea 备份码是什么_EA的原始访问是什么,值得吗?

ea 备份码是什么EA’s Origin Access gives you access to more than 70 games, discounts, and new EA games before they’re released for a monthly (or yearly) subscription fee. But is it really worth it? EA的Origin Access可让您访问70多种游戏&#xff0c;打折游戏…