并不理解。但是毕竟也做了一些题,略微小结。

注:这里讨论的暂时是有向图的强联通分量。

先贴出模板。学长:我也不理解,但我可以叫你们怎么背代码。

 #include<cstdio>
#include<algorithm>
#include<stack>
#define maxn using namespace std;
int dfn[maxn],low[maxn] void dfs(int p)
{
dfn[p]=low[p]=++dfs_clock;
s.push(p);
for(int i=head[p];i;i=edge[i].next)
{
int y=edge[i].to;
if(!dfn[y])
{
dfs(y);
low[p]=min(low[p],low[y]);
}
else if(!scc[y]) low[p]=min(low[p],dfn[y]);
}
if(dfn[p]==low[p])
{
scc_cnt++;
while()
{
int x=s.top();
s.pop();
scc[x]=scc_cnt;
if(x==p) break;
}
}
} int main()
{
scanf("%d",&n);
for(int i=;i<=m;i++)
//读入边信息
for(int i=;i<=n;i++)
{
if(!dfn[i]) dfs(i);
tong[scc[i]]++;
//scc[i]->i点在哪个强连通分量
//tong[i]-> 第i个强连通分量大小
}
return ;
}

一 缩点

一句话来说,就是求出有向图中的强联通分量后,把每个强联通分量用一个点代替,得到一个DAG(有向无环图)。

我们用一个新的邻接表来记录新的DAG上的边。

这个过程可以近似的理解为缩点。先放下求缩点的模板。

 #include<cstdio>
#include<algorithm>
#include<stack>
#include<queue>
#include<cstring> using namespace std;
//tarjan_need
int n,m,x,y,tot,qwq,ans,scc_cnt,dfs_clock,sum[],pv[],head[],head_DAG[],low[],dfn[],scc[];
struct node{
int next,to;
}edge[];
struct nodee{
int next,to;
}edge_DAG[];
stack<int>s; void add(int x,int y,int op)
{
if(op==)
{
edge[++tot].to=y;
edge[tot].next=head[x];
head[x]=tot;
}
else if(op==)
{
edge_DAG[++qwq].to=y;
edge_DAG[qwq].next=head_DAG[x];
head_DAG[x]=qwq;
}
} void tarjan(int p)
{
dfn[p]=low[p]=++dfs_clock;
s.push(p);
for(int i=head[p];i;i=edge[i].next)
{
int y=edge[i].to;
if(!dfn[y])
{
tarjan(y);
low[p]=min(low[p],low[y]);
}
else if(!scc[y]) low[p]=min(low[p],dfn[y]);
}
if(dfn[p]==low[p])
{
scc_cnt++;
while()
{
int x=s.top();
s.pop();
scc[x]=scc_cnt;
if(x==p) break;
}
}
} int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++) scanf("%d",&pv[i]);
for(int i=;i<=m;i++)
scanf("%d%d",&x,&y),add(x,y,);
for(int i=;i<=n;i++)
if(!dfn[i]) tarjan(i);
for(int x=;x<=n;x++)
for(int i=head[x];i;i=edge[i].next)
{
int y=edge[i].to;
if(scc[x]!=scc[y])
add(scc[x],scc[y],);
}
return ;
}

然鹅洛谷的模板题更深一步,求:

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

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

我们跑一遍tarjan进行缩点,得到一个新的DAG,在DAG上枚举起点,各跑一遍spfa求最长路,每跑完再枚举终点。注意这里是点带权而不是边带权,在进行强联通分量个数划分的时候,我们需要更新合并每个SCC的点权和(注意用新的数组保存!被这个地方卡了)。

(其实还有一种缩点后dp的做法,用拓扑排序处理掉dp后效性

 #include<cstdio>
#include<algorithm>
#include<stack>
#include<queue>
#include<cstring> using namespace std;
//tarjan_need
int n,m,x,y,tot,qwq,ans,scc_cnt,dfs_clock,sum[],pv[],head[],head_DAG[],low[],dfn[],scc[];
struct node{
int next,to;
}edge[];
struct nodee{
int next,to;
}edge_DAG[];
stack<int>s;
//spfa_need
int dis[];
bool vis[]; void add(int x,int y,int op)
{
if(op==)
{
edge[++tot].to=y;
edge[tot].next=head[x];
head[x]=tot;
}
else if(op==)
{
edge_DAG[++qwq].to=y;
edge_DAG[qwq].next=head_DAG[x];
head_DAG[x]=qwq;
}
} void tarjan(int p)
{
dfn[p]=low[p]=++dfs_clock;
s.push(p);
for(int i=head[p];i;i=edge[i].next)
{
int y=edge[i].to;
if(!dfn[y])
{
tarjan(y);
low[p]=min(low[p],low[y]);
}
else if(!scc[y]) low[p]=min(low[p],dfn[y]);
}
if(dfn[p]==low[p])
{
scc_cnt++;
while()
{
int x=s.top();
s.pop();
scc[x]=scc_cnt;
sum[scc_cnt]+=pv[x];
if(x==p) break;
}
}
} void spfa(int start)
{
memset(dis,,sizeof(dis));
memset(vis,,sizeof(vis));
queue<int>q;
q.push(start);dis[start]=sum[start];vis[start]=;
while(!q.empty())
{
int x=q.front();q.pop();vis[x]=;
for(int i=head_DAG[x];i;i=edge_DAG[i].next)
{
int y=edge_DAG[i].to;
if(dis[y]<dis[x]+sum[y])
{
dis[y]=dis[x]+sum[y];
if(!vis[y])
{
vis[y]=;
q.push(y);
}
}
}
}
for(int i=;i<=scc_cnt;i++) ans=max(ans,dis[i]);
} int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++) scanf("%d",&pv[i]);
for(int i=;i<=m;i++)
scanf("%d%d",&x,&y),add(x,y,);
for(int i=;i<=n;i++)
if(!dfn[i]) tarjan(i);
for(int x=;x<=n;x++)
for(int i=head[x];i;i=edge[i].next)
{
int y=edge[i].to;
if(scc[x]!=scc[y])
add(scc[x],scc[y],);
}
for(int i=;i<=scc_cnt;i++) spfa(i);
printf("%d",ans);
return ;
}

二、受欢迎的牛

题目描述

每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶

牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果A喜

欢B,B喜欢C,那么A也喜欢C。牛栏里共有N 头奶牛,给定一些奶牛之间的爱慕关系,请你

算出有多少头奶牛可以当明星。

题意可以再简化: 给定一个有向图,问有多少个顶点是由任何顶点出发都可达的。

分析:(抄lyc课件)

• 有向无环图中唯一出度为 0 的点,一定可以由任何点出发均可达
• 由于无环,所以从任何点出发往前走,必然终止于一个出度为0
的点

• 求出所有强连通分量,每个强连通分量缩成一点,形成一个有向
无环图DAG。
• DAG上面如果恰有一个出度为0的点,说明DAG所有的点可到达这
个点,这个点是原图中的强连通分量,该强连通分量的点数,就
是答案。
• DAG上面如果有不止一个出度为0的点,因为它们不能互相到达,
故原问题无解,答案为0

code

 #include<cstdio>
#include<algorithm>
#include<stack> using namespace std; int n,m,tot,qwq,Chemist,cellur,dfs_clock,scc_cnt,qaq,qaqaq;
int head[],head_DAG[],dfn[],low[],scc[],du[],tong[];
struct node{
int to,next;
}edge[];
stack<int>s; void add(int x,int y,int op)
{
if(op==)
{
edge[++tot].to=y;
edge[tot].next=head[x];
head[x]=tot;
}
} void dfs(int p)
{
dfn[p]=low[p]=++dfs_clock;
s.push(p);
for(int i=head[p];i;i=edge[i].next)
{
int y=edge[i].to;
if(!dfn[y])
{
dfs(y);
low[p]=min(low[p],low[y]);
}
else if(!scc[y]) low[p]=min(low[p],dfn[y]);
}
if(dfn[p]==low[p])
{
scc_cnt++;
while()
{
int x=s.top();
s.pop();
scc[x]=scc_cnt;
if(x==p) break;
}
}
} int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=m;i++)
scanf("%d%d",&Chemist,&cellur),add(Chemist,cellur,);
for(int i=;i<=n;i++)
{
if(!dfn[i]) dfs(i);
tong[scc[i]]++;
}
for(int x=;x<=n;x++)
for(int i=head[x];i;i=edge[i].next)
{
int y=edge[i].to;
if(scc[x]!=scc[y]) du[scc[x]]++;
} for(int i=;i<=scc_cnt;i++)
if(du[i]==) qaq++,qaqaq=i; if(qaq==) printf("%d",tong[qaqaq]);
else printf("");
return ;
}

三、信息传递

题目描述

有 n 个同学(编号为 1 到 n)正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,

其中,编号为 ii 的同学的信息传递对象是编号为 Ti​ 的同学。

游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息, 但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自 己的生日时,游戏结束。请问该游戏一共可以进行几轮?

题意也是比较明白,求图中的最小环,我们可以用tarjan求(虽然有些小题大做的嫌疑)。记录各个强联通分量的大小,最后找最小的大于1的环就行了。

code

 #include<cstdio>
#include<algorithm>
#include<stack>
#define maxn 200090 using namespace std; int n,y,tot,ans=,dfs_clock,scc_cnt,tong[maxn],dfn[maxn],low[maxn],head[maxn],scc[maxn];
stack<int>s;
struct node{
int to,next;
}edge[maxn]; void add(int x,int y)
{
edge[++tot].to=y;
edge[tot].next=head[x];
head[x]=tot;
} void dfs(int p)
{
dfn[p]=low[p]=++dfs_clock;
s.push(p);
for(int i=head[p];i;i=edge[i].next)
{
int y=edge[i].to;
if(!dfn[y])
{
dfs(y);
low[p]=min(low[p],low[y]);
}
else if(!scc[y]) low[p]=min(low[p],dfn[y]);
}
if(dfn[p]==low[p])
{
scc_cnt++;
while()
{
int x=s.top();
s.pop();
scc[x]=scc_cnt;
if(x==p) break;
}
}
} int main()
{
scanf("%d",&n);
for(int i=;i<=n;i++)
scanf("%d",&y),add(i,y);
for(int i=;i<=n;i++)
{
if(!dfn[i]) dfs(i);
tong[scc[i]]++;
}
for(int i=;i<=scc_cnt;i++) if(tong[i]>&&tong[i]<ans) ans=tong[i];
printf("%d\n",ans);
return ;
}

强连通分量初探 By cellur925的更多相关文章

  1. 强连通分量再探 By cellur925

    我真的好喜欢图论啊. (虽然可能理解的并不深hhh) 上一次(暑假)我们初探了强联通分量,这一次我们再探.(特别感谢pku-lyc老师的课件.有很多引用) 上次我们忘记讨论复杂度了.tarjan老爷爷 ...

  2. 强连通分量算法·$tarjan$初探

    嗯,今天好不容易把鸽了好久的缩点给弄完了--感觉好像--很简单? 算法的目的,其实就是在有向图上,把一个强连通分量缩成一个点--然后我们再对此搞搞事情,\(over\) 哦对,时间复杂度很显然是\(\ ...

  3. Tarjan算法初探 (1):Tarjan如何求有向图的强连通分量

    在此大概讲一下初学Tarjan算法的领悟( QwQ) Tarjan算法 是图论的非常经典的算法 可以用来寻找有向图中的强连通分量 与此同时也可以通过寻找图中的强连通分量来进行缩点 首先给出强连通分量的 ...

  4. 图论之tarjan真乃神人也,强连通分量,割点,桥,双连通他都会

    先来%一下Robert Tarjan前辈 %%%%%%%%%%%%%%%%%% 然后是热情感谢下列并不止这些大佬的博客: 图连通性(一):Tarjan算法求解有向图强连通分量 图连通性(二):Tarj ...

  5. HDU5934 强连通分量

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=5934 根据距离关系建边 对于强连通分量来说,只需引爆话费最小的炸弹即可引爆整个强连通分量 将所有的强连通分 ...

  6. POJ1236Network of Schools[强连通分量|缩点]

    Network of Schools Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 16571   Accepted: 65 ...

  7. 有向图的强连通分量的求解算法Tarjan

    Tarjan算法 Tarjan算法是基于dfs算法,每一个强连通分量为搜索树中的一颗子树.搜索时,把当前搜索树中的未处理的结点加入一个栈中,回溯时可以判断栈顶到栈中的结点是不是在同一个强连通分量中.当 ...

  8. Tarjan算法--强连通分量

    tarjan的过程就是dfs过程. 图一般能画成树,树的边有三种类型,树枝边 + 横叉边(两点没有父子关系) + 后向边(两点之间有父子关系): 可以看到只有后向边能构成环,即只有第三张图是强连通分量 ...

  9. 强连通分量的一二三 | | JZOJ【P1232】 | | 我也不知道我写的什么

    贴题: 在幻想乡,上白泽慧音是以知识渊博闻名的老师.春雪异变导致人间之里的很多道路都被大雪堵塞,使有的学生不能顺利地到达慧音所在的村庄.因此慧音决定换一个能够聚集最多人数的村庄作为新的教学地点.人间之 ...

随机推荐

  1. 2015轻院校赛 B 迷宫 (bfs)

    http://acm.zznu.edu.cn/problem.php?id=1967 这套题的有毒   我交了好多遍才对 坑:机关要按照顺序走 并且在走这个机关之前不能走这个机关  但是能穿过这个机关 ...

  2. system表空间用满解决

      分类: Oracle 早上看到alert日志报说system表空间快满了(oracle版本是11gR2):   如果system表空间不是自动扩展,空间用满甚至会出现数据库无法登陆.使用任何用户登 ...

  3. TeamCity - Docker创建

    // 创建Server docker run -it --name teamcity-server-instance \-v /home/tc_datadir:/data/teamcity_serve ...

  4. [教程]Delphi 中三种回调函数形式解析

    Delphi 支持三种形式的回调函数 全局函数这种方式几乎是所有的语言都支持的,类的静态函数也可以归为此类,它保存的只是一个函数的代码起始地址指针( Pointer ).在 Delphi 中声明一般为 ...

  5. 【深度探索c++对象模型】Function语义学之成员函数调用方式

    非静态成员函数 c++的设计准则之一就是:非静态成员函数至少和一般的非成员函数有相同的效率.编译器内部已将member函数实体转换为对等的nonmember函数实体. 转化步骤: 1.改写函数原型以安 ...

  6. 学习LaTex

    MarkDown+Latex 本来想学习latex编辑公式的,在博客园内置的MarkDown编辑器已经支持Latex公式解析了,如下: $$x=\frac{-b\pm\sqrt{b^2-4ac}}{2 ...

  7. MyBatis -- sql映射文件具体解释

    MyBatis 真正的力量是在映射语句中. 和对等功能的jdbc来比价,映射文件节省非常多的代码量. MyBatis的构建就是聚焦于sql的. sql映射文件有例如以下几个顶级元素:(按顺序) cac ...

  8. PHPCMS中GET标签概述、 get 标签语法、get 标签创建工具、get 调用本系统演示样例、get 调用其它系统演示样例

    一.get 标签概述 通俗来讲,get 标签是Phpcms定义的能直接调用数据库里面内容的简单化.友好化代码,她可调用本系统和外部数据,仅仅有你对SQL有一定的了解,她就是你的绝世好剑!也就是适合熟悉 ...

  9. WPF 创建二维码

    1.在http://zxingnet.codeplex.com/网站上下载ZXing .Net的第三方库 2.新建一个WPFproject 3.引入zxing.dll 4.加入引用空间 using Z ...

  10. C++常用的函数,好的博客文章整理,集锦

    http://www.cnblogs.com/xianghang123/archive/2011/08/24/2152404.html    c语言产生随机数的方法 http://blog.sina. ...