整理的算法模板合集: ACM模板


目录

  • tarjan模板
  • 最大半连通子图
  • tarjan缩点+记忆化搜索
  • 几个结论

tarjan缩点以后的点(强连通分量)

  • 要么出度和入度都为0
  • 要么有出度,入度为0
  • 要么有入度,出度为0
  • 要么入度和出度都不为 0,但是对于同一个点的入度和出度不 同时 非0

tarjan模板

题目大意

被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果A 喜欢 B,B 喜欢 C,那么 A 也喜欢 C。牛栏里共有 N 头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。

大体思路

受欢迎的奶牛只有可能是图中唯一的出度为零的强连通分量中的所有奶牛,所以若出现两个以上出度为0的强连通分量则不存在明星奶牛,因为那几个出度为零的分量的爱慕无法传递出去。那唯一的分量能受到其他分量的爱慕同时在分量内相互传递,所以该分量中的所有奶牛都是明星。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<vector>using namespace std;
typedef long long ll;
const int N = 500007, M = 5000007, INF = 0x3f3f3f3f;int n, m;
int dfn[N], low[N];
int ind;
bool ins[N];
int stk[N], top;
int head[N], nex[M], ver[M], edge[M], tot;
int cnt_scc;
vector<int>scc[N];
int scc_num[N];//个数
int scc_id[N];//idvoid add(int x, int y)
{ver[tot] = y;nex[tot] = head[x];head[x] = tot ++ ;
}void tarjan(int x)
{dfn[x] = low[x] = ++ ind;stk[++ top] = x;ins[x] = 1;for(int i = head[x]; ~i; i = nex[i]){int y = ver[i];if(!dfn[y]){tarjan(y);low[x] = min(low[x], low[y]);}else if(ins[y])low[x] = min(low[x], dfn[y]);}if(dfn[x] == low[x]){//说明回来了cnt_scc ++ ;int y ;do{y = stk[top -- ], ins[y] = 0;scc_id[y] = cnt_scc;scc[cnt_scc].push_back(y);scc_num[cnt_scc] ++ ;}while(x != y);}
}int out[M];int main()
{memset(head, -1, sizeof head);scanf("%d%d", &n, &m);for(int i = 1; i <= m;++ i){int x, y;scanf("%d%d", &x, &y);add(x, y);}for(int i = 1; i <= n; ++ i){if(!dfn[i])tarjan(i);}//找有多少个出度为0的点for(int i = 1; i <= n; ++ i){for(int j = head[i]; ~j; j = nex[j]){int k = ver[j];if(scc_id[i] != scc_id[k]){out[scc_id[i]] ++ ;//该强连通分量缩点以后的这个点的出度 ++}}}int ans = 0;for(int i = 1; i <= cnt_scc; ++ i){if(!out[i]){//有一个出度为0 的既是答案if(!ans)ans = i;else {puts("0");return 0;}}}printf("%d\n", scc_num[ans]);return 0;
}

一般用到tarjan算法的题目步骤都非常相似:

  1. tarjan算法
  2. 缩点,建图
  3. 按照拓扑序递推(这里缩点以后就已经是逆拓扑序了)/ 循环遍历新图求解答案。

最大半连通子图

这一道是我认为非常经典的一道有向图tarjan算法模板例题,包含了:

  • 有向图的tarjan模板
  • 去重缩点模板
  • 按拓扑序递推模板

/*[ZJOI2007]最大半连通子图*/
const int N = 100007, M = 2000007, INF = 0x3f3f3f3f;typedef long long ll;
int mod;
int n, m;
int dfn[N], low[N], num;
int ver[M], edge[M], head[N], nex[M], tot;
int h[N];int Size[N], scc_cnt, scc_id[N];
int stk[N], top, ins[N];int f[N], g[N];//顶点个数和方案数void add(int h[],int x,int y){ver[tot] = y;nex[tot] = h[x];h[x] = tot ++ ;
}
//!有向图的tarjan模板
void tarjan(int x){dfn[x] = low[x] = ++ num;stk[ ++ top] = x;ins[x] = true;for(int i = head[x];~i;i = nex[i]){int y = ver[i];if(!dfn[y]){tarjan(y);low[x] = min(low[x],low[y]);}else if(ins[y]){low[x] = min(low[x], dfn[y]);}}if(dfn[x] == low[x]){int y;++ scc_cnt;do{y = stk[top -- ];ins[y] = false;scc_id[y] = scc_cnt;Size[scc_cnt] ++ ;}while(x != y);}
}int main()
{memset(head,-1,sizeof head);memset(h,-1,sizeof h);scanf("%d%d%d",&n,&m,&mod);while(m -- ){int x,y;scanf("%d%d",&x,&y);add(head,x,y);}for(int i = 1;i <= n;++i)if(!dfn[i])tarjan(i);unordered_set<ll>st;//(u,v) -> u * 1000000 + v;//因为一共只有100000个点for(int i = 1;i <= n;++i){for(int j = head[i];~j;j = nex[j]){int k = ver[j];int a = scc_id[i], b = scc_id[k];ll hash = a * 100000ll + b;//!去重缩点模板//建新图并判重if(a != b && !st.count(hash)){add(h,a,b);st.insert(hash);}}}//!按拓扑序递推模板for(int i = scc_cnt;i >= 1;-- i){//建完的图是逆拓扑序,按照拓扑序递推,所以必须倒着来if(!f[i]){f[i] = Size[i];g[i] = 1;}for(int j = h[i];~j;j = nex[j]){int k = ver[j];if(f[k] < f[i] + Size[k]){f[k] = f[i] + Size[k];g[k] = g[i];}else if(f[k] == f[i] + Size[k]){g[k] = (g[k] + g[i]) % mod;}}}ll maxx = 0,sum = 0;for(int i = 1;i <= n;++i){if(f[i] > maxx){maxx = f[i];sum = g[i];}else if(f[i] == maxx){sum = (sum + g[i] ) % mod;}}printf("%lld\n%lld\n",maxx, sum);return 0;
}

tarjan缩点+记忆化搜索

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>using namespace std;const int N = 500007, M = 5000007, INF = 0x3f3f3f3f;int n, m;
int dfn[M], low[M], num;
int head[N], ver[M], nex[M], tot;
int hc[N], vc[M], nc[M], tc;
int scc_cnt;
int scc_id[N];
bool vis[N];void add(int x, int y)
{ver[tot] = y;nex[tot] = head[x];head[x] = tot ++ ;
}void add_c(int x, int y)
{vc[tc] = y;nc[tc] = hc[x];hc[x] = tc ++ ;
}int w[N];
int val[N];
int stk[N], top;
bool ins[N];
int f[M];
void tarjan(int x)
{dfn[x] = low[x] = ++ num;stk[++ top] = x;ins[x] = true;for(int i = head[x]; ~i; i = nex[i]){int y = ver[i];if(!dfn[y]){tarjan(y);low[x] = min(low[x], low[y]);}else if(ins[y])low[x] = min(low[x], dfn[y]);}if(low[x] == dfn[x]){int y;scc_cnt ++ ;do{y = stk[top -- ];ins[y] = false;scc_id[y] = scc_cnt;val[scc_cnt] += w[y];}while(x != y);}return ;
}//记忆化搜索
void dfs(int x)
{if(f[x])return ;f[x] = val[x];int res = 0;for(int i = hc[x] ;~i;i = nc[i]){int y = vc[i];if(!f[y])dfs(y);res = max(res, f[y]);}f[x] += res;//选择一个最大的分支(因为只能走一条路径)
}int main()
{scanf("%d%d", &n, &m);for(int i = 1; i <= n; ++ i)scanf("%d", &w[i]);memset(head, -1, sizeof head);memset(hc, -1, sizeof hc);for(int i = 1; i<= m; ++i){int x, y;scanf("%d%d", &x, &y);add(x, y);}for(int i = 1; i <= n; ++ i)if(!dfn[i])tarjan(i);for(int x = 1; x <= n; ++ x){for(int i = head[x]; ~i; i = nex[i]){int y = ver[i];if(scc_id[x] != scc_id[y])add_c(scc_id[x], scc_id[y]);}}memset(vis, 0, sizeof vis);for(int i = 1; i <= scc_cnt; ++ i)if(!f[i])dfs(i);int ans = 0;for(int i = 1; i <= scc_cnt;++ i)ans = max(ans, f[i]);printf("%d\n", ans);return 0;
}

几个结论

有向图

  • 一个有向图最少给多少个点提供资源(有连边的点之间可以互相传达)使得所有的点都拥有资源

结论:缩点以后入度为0的点的个数(因为入度为0则它不能被其他的点支援,所以只能直接给它资源)

  • 连多少条边使得整个有向图变为强连通图

结论:max{p,q}max\{p,q\}max{p,q}(缩点以后,入度为0的点的数量记为p,出度为0的点的数量记为q)

  • 最大可以增加多少条边使得这个图仍然不是强连通图

结论:n∗(n−1)−m−min∗(n−minv)n * (n - 1) - m - min * (n - minv)n∗(n−1)−m−min∗(n−minv)(其中n为点数、m为边数、minv为缩点以后的点中入度和出度至少有一个为0的包含节点个数最少的点)

int n, m;
int dfn[N], low[N], num;
int ins[N], stk[N], top;
int head[N], edge[M], nex[N], ver[N], tot;
int hs[N];
int scc_cnt, Size[N], scc_id[N];
int out[N], in[N];void add(int h[], int x, int y )
{ver[tot] = y;nex[tot] = h[x];h[x] = tot ++ ;
}void tarjan(int x)
{dfn[x] = low[x] = ++num;stk[++ top] = x;ins[x] = true;for(int i = head[x];~i ;i = nex[i]){int y = ver[i];if(!dfn[y]){tarjan(y);low[x] = min(low[x], low[y]);}else if(ins[y])low[x] = min(low[x], dfn[y]);}if(dfn[x] == low[x]){++scc_cnt;int y;do{y = stk[top -- ];ins[y] = false;scc_id[y] = scc_cnt;Size[scc_cnt] ++;}while(x != y);}
}int main()
{memset(head,-1,sizeof head);memset(hs,-1,sizeof hs);scanf("%d",&n);for(int i = 1;i <= n;++i){int b;while(~scanf("%d",&b) && b){add(head,i,b);}}for(int i = 1;i <= n;++i)if(!dfn[i])tarjan(i);for(int x = 1;x <= n;++x){for(int i = head[x];~i;i = nex[i]){int y = ver[i];int a = scc_id[x],b = scc_id[y];if(a != b){add(hs,a,b);}}}for(int x = 1;x <= scc_cnt;++x){for(int i = hs[x];~i;i = nex[i]){int y = ver[i];out[x] ++ ;in[y] ++ ;}}int maxo = 0;int maxi = 0;for(int x = 1;x <= scc_cnt;++x){if(!out[x])maxo ++ ;if(!in[x])maxi ++ ;}printf("%d\n",maxi);if(scc_cnt == 1)puts("0");else printf("%d\n",max(maxo,maxi));return 0;
}

模板 - 有向图的连通性相关推荐

  1. 离散数学 --- 图论基础 --- 无向图的连通性和有向图的连通性

    第一部分 ---- 无向图的连通性 1.完全图:任意两个结点之间都有边 2.零图:所有结点都是孤立结点 3.平凡图:仅有一个结点的图 1.等价类:R是集合上的一个等价关系,选定集合中的任意一个元素a, ...

  2. 【数据结构】无向图与有向图的连通性及相关算法

    I. 阅读前你所需的基础知识 了解图 (graph) 的基本知识 (什么是顶点, 什么是边, 什么是路径等等) 了解图的深度优先遍历 (DFT, Depth-first Traversal) 了解有向 ...

  3. 图论-有向图的连通性模板题(hdu1296)(hdu1827)

    1.强连通分量: 强连通分量可以理解为边数最少的情况下是一个环. 这里写了一个模板题用的是tarjan算法,当然还有其他算法. tarjan算法的关键其实还是对于num数组和low数组的使用 然后可以 ...

  4. 模板 - 无向图的连通性

    整理的算法模板合集: ACM模板 目录 tarjan算法求无向图的桥.边双连通分量并缩点 tarjan算法求无向图的割点.点双连通分量并缩点 结论:变成边双连通分量所需要新建的边数 动态求解当前图中的 ...

  5. 我的所有优质博客全部开源啦(我自己原创的《ACM模板》《算法全家桶》《算法竞赛中的初等数论》 PDF免费下载)

    你好呀ヾ(≧▽≦*)o 我是繁凡さん 这两年来我写了很多长篇文章,主要涉及数据结构,算法,程序设计竞赛,数学,计算几何等方面的内容: <数据结构>C语言版(清华严蔚敏考研版) 全书知识梳理 ...

  6. ACM模板(满注释模板!)

    转自:https://fanfansann.blog.csdn.net/article/details/105493218 目录 F o r e w o r d ForewordForeword T ...

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

    Tarjan 算法与有向图的连通性 Tarjan 算法是基于对图进行深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树.搜索时,把当前搜索树中未处理的节点加入一个栈,回溯时可以判断栈顶到栈中的节点 ...

  8. 按要求自动生成无向/有向图(基于C++实现)

    按照要求自动生成无向/有向图(基于C++实现) 最小连通子图生成算法 程序的思路和伪代码 主函数代码 程序运行结果展示 本文内容是按照图的类型(随机有向图 无向连通图.节点个数N.边个数M.以及边的权 ...

  9. 图的连通性和连通分量

    1.无向图的连通性 运用深度优先搜索或广度优先搜索遍历无向图可以分析图的连通性.可通过额外设置计数器count(初始值0)统计出图的连通分量,每调用一次,计数器count增1.当遍历完无向图时,若co ...

最新文章

  1. linux 内核探测kprobe 初步了解
  2. 程序员求职之道(《程序员面试笔试宝典》)之学业与求职,孰轻孰重?
  3. Redis HyperLogLog常用命令
  4. 快来!前端君喊你加入快驾网智囊团
  5. Python中的函数(调用、参数、返回值、变量的作用域)
  6. 【Vue】详解 SFC 与 vue-loader
  7. tensorflow之读取jpg图像保存为tfrecord再读取
  8. 关于 Maven 的插件maven-war-plugin
  9. STL vector的迭代器的熟练运用及lower_bound和upper_bound的使用
  10. 容器技术Docker K8s 33 04-容器服务ACK基础与进阶-06-集群管理
  11. 【OpenCV-Python】29.OpenCV的特征检测——特征匹配
  12. wⅰndows ISO文件备份,5 款 Windows 最佳备份软件
  13. BMVC 2020 Keynote 消除数据集偏见
  14. LaTeX中实心圆点列表的一点经验
  15. word软件在计算机哪里,电脑自带的word在哪里
  16. 如何快速调出软键盘_软键盘怎么调出来 打开软键盘的方法【图文】
  17. java 调用企查查API查询企业信息
  18. 医学用计算机吗,学临床医学必须要用笔记本电脑吗?
  19. Python之父愤然退位:再也无法忍受他们鄙视我的意见
  20. 苹果电池显示维修_苹果财大气粗:维修换电池?直接给你换个机

热门文章

  1. 机器人视觉三维成像技术全解析
  2. 基础 | 深度学习与神经网络-介绍
  3. 基于OpenCV的车辆变道检测
  4. Flutter使用CupertinoAlertDialog 报 'alertDialogLabel' was called on null.
  5. 理解什么是MyBatis?
  6. AC日记——欧几里得的游戏 洛谷 P1290
  7. 高斯判别分析 Gaussian Discriminant Analysis
  8. SharePoint 2013 处理videoplayerpage.aspx下的个人图片显示有误问题
  9. XSL 扩展样式表语言(EXtensible Stylesheet Language)
  10. arcgis for server 登陆manager失败解决办法