说明

在一般图中,求解最长路或最短路只能通过最短路算法解决

但是在DAG中,由于不存在环,因此可以通过递推,以线性复杂度计算处最长路或最短路。当然需要首先对有向图进行Tarjan缩点转化为DAG

例题

题目理解

首先对题目中涉及到的几个概念做几点说明:

  1. 半连通:任意点对\((u, v)\),满足\(u->v\) \(v->u\)有路径。因为是满足的,所以强连通满足半连通

    因此若要满足半连通,只需没有孤立点即可
  2. 导出子图:图\(G\)的导出子图是指,\(G\)顶点的一个子集\(E\)和\(G\)中两端顶点均在\(E\)中的边构成的图

如果理解了半连通和导出子图的定义,有一定概率我们可以想到如果将原图转换为DAG,因为图上边权是一致的,因此包含最多节点数目的一条路径是最长路

如果可以想到这个结论,下一步的想法很自然的就是去判断最长路是否为最大半连通子图,有以下两点因素需要考虑:

  1. 最长路是否为半连通的

    因为强连通是半连通的且单向链路是半连通的,而Tarjan缩点后图上最长路是一条包含强连通分量的单向链路,因此最长路是半连通的
  2. 最长路是否为导出子图

    若最长路中的两点之间存在重边,显然最长路只会包含重边中的一条,但根据导出子图的定义,导出子图应当将该两点中的重边全部包含在内,因此最长路并非导出子图

    虽然最长路并非最大半连通子图,但是可以发现,最长路不满足最大半连通子图的原因在于没有选择一些重边,假设我们加上这些重边,那么最长路显然就成为了最大半连通子图。而且我们可以发现,虽然加上了这些边,但最长路中点的数量并没有发生改变

综上所述,实际结论为:最大半连通子图拥有的节点数 $K == $ 有向有环图转换为DAG后最长路的长度,不同的最大半连通子图的数目$C == $有向有环图转换为DAG后最长路的条数

代码实现

  1. Tarjan求强连通分量缩点,将原图转换为DAG
  2. 利用DP思想递推求解最长路长度和最长路条数

    f[i]: 统计走到i点时获得的最大长度

    g[i]:统计以最大长度走到i点时的方案数

    设i点的前驱节点有一个j,则操作逻辑如下
// 从j点走来总长度大于当前最大长度,则选择从j点走来
if (f[j] + Size[i] > f[i])
{
f[i] = f[j] + Size[i];
g[i] = f[j];
}
// 从j点走来总长度与当前最大长度相等,则走到i点既可以按现在的方案也可以选择从j点走来
else if (f[j] + Size[i] == f[i])
g[i] += g[j];
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <unordered_map> using namespace std;
using LL = long long; const int N = 1e5 + 10, M = 2e6 + 10; // M应当包含原图和缩点后的图,边数应当*2 int n, m, mod;
int h[N], hs[N], e[M], ne[M], idx;
stack<int> stk;
bool in_stk[N];
int dfn[N], low[N], timestamp;
int id[N], Size[N], scc_cnt;
unordered_map<LL, bool> st;
int f[N], g[N]; void add(int *h, int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
void tarjan(int u)
{
dfn[u] = low[u] = ++ timestamp;
stk.push(u); in_stk[u] = true; for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (!dfn[j])
{
tarjan(j);
low[u] = min(low[u], low[j]);
}
else if (in_stk[j]) low[u] = min(low[u], dfn[j]);
} if (dfn[u] == low[u])
{
int y;
++ scc_cnt;
do {
y = stk.top(); stk.pop();
in_stk[y] = false;
id[y] = scc_cnt;
++ Size[scc_cnt];
} while (y != u);
}
}
int main()
{
memset(h, -1, sizeof h);
memset(hs, -1, sizeof hs); cin >> n >> m >> mod;
for (int i = 0; i < m; ++ i)
{
int a, b;
cin >> a >> b;
add(h, a, b);
} for (int i = 1; i <= n; ++ i)
if (!dfn[i])
tarjan(i); for (int i = 1; i <= n; ++ i)
for (int j = h[i]; ~j; j = ne[j])
{
int k = e[j];
int a = id[i], b = id[k];
LL hash = a * 1000000ll + b; // 可能会爆int,需要ll if (a != b && !st[hash])
{
add(hs, a, b);
st[hash] = true;
}
} /**
* 递推需要按照拓扑序进行,否则数据无法更新完全
* scc_cnt越小,则其在拓扑序中越靠后
* scc_cnt从大到小的顺序即为拓扑序的顺序
*/
for (int i = scc_cnt; i >= 1; -- i)
// for (int i = 1; i <= scc_cnt; ++ i)
{
if (!f[i])
{
f[i] = Size[i];
g[i] = 1;
}
for (int j = hs[i]; ~j; j = ne[j]) // 注意这里的节点是指缩点之后的节点,需要使用hs
{
int p = e[j];
if (f[p] < f[i] + Size[p])
{
f[p] = f[i] + Size[p];
g[p] = g[i];
}
else if (f[p] == f[i] + Size[p])
g[p] = (g[p] + g[i]) % mod;
}
} int maxf = -0x3f3f3f3f, maxg = -0x3f3f3f3f;
for (int i = 1; i <= scc_cnt; ++ i)
if (f[i] > maxf)
{
maxf = f[i];
maxg = g[i];
}
else if (f[i] == maxf)
maxg = (maxg + g[i]) % mod; cout << maxf << endl << maxg << endl; return 0;
}

递推求解DAG最长路径长度及最长路径条数的更多相关文章

  1. 九度OJ 1205 N阶楼梯上楼问题 -- 动态规划(递推求解)

    题目地址:http://ac.jobdu.com/problem.php?pid=1205 题目描述: N阶楼梯上楼问题:一次可以走两阶或一阶,问有多少种上楼方式.(要求采用非递归) 输入: 输入包括 ...

  2. HDU 2041--超级楼梯(递推求解)

    Description 有一楼梯共M级,刚开始时你在第一级,若每次只能跨上一级或二级,要走上第M级,共有多少种走法?   Input 输入数据首先包含一个整数N,表示测试实例的个数,然后是N行数据,每 ...

  3. 题目1205:N阶楼梯上楼问题(2008年华中科技大学计算机保研机试真题:递推求解)

    题目1205:N阶楼梯上楼问题 时间限制:1 秒 内存限制:128 兆 特殊判题:否 提交:2447 解决:927 题目描写叙述: N阶楼梯上楼问题:一次能够走两阶或一阶,问有多少种上楼方式. (要求 ...

  4. [arc082f]Sandglass 递推

    Description 有一个沙漏由两个上下相通玻璃球A和B构成,这两个玻璃球都含有一定量的沙子,我们暂且假定AB中位于上方的玻璃球的为U,下方的玻璃球为L,则除非U中没有沙子,否则每秒钟都会有1克沙 ...

  5. hdu 2050 折线分割平面 (递推)

    折线分割平面 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Subm ...

  6. 算法笔记_091:蓝桥杯练习 递推求值(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 已知递推公式: F(n, 1)=F(n-1, 2) + 2F(n-3, 1) + 5, F(n, 2)=F(n-1, 1) + 3F(n- ...

  7. ACM-ICPC 2018 徐州赛区网络预赛 A.Hard to prepare 【规律递推】

    任意门:https://nanti.jisuanke.com/t/31453 A.Hard to prepare After Incident, a feast is usually held in ...

  8. [hdu 2604] Queuing 递推 矩阵快速幂

    Problem Description Queues and Priority Queues are data structures which are known to most computer ...

  9. C. The Fair Nut and String 递推分段形dp

    C. The Fair Nut and String 递推分段形dp 题意 给出一个字符串选择一个序列\({p_1,p_2...p_k}\)使得 对于任意一个\(p_i\) , \(s[p_i]==a ...

  10. ACM学习历程—SNNUOJ 1116 A Simple Problem(递推 && 逆元 && 组合数学 && 快速幂)(2015陕西省大学生程序设计竞赛K题)

    Description Assuming a finite – radius “ball” which is on an N dimension is cut with a “knife” of N- ...

随机推荐

  1. 用shape-outside实现文字环绕、CSS圆锥色彩渐变、指定背景在容器上如何附着、长度单位vh

    用shape-outside实现文字环绕 实现效果: 介绍:shape-outside是让文字从图片外部开始环绕,以及设置图片的形状. 函数定义: circle(size at x y);用于制作圆形 ...

  2. 前端入门知识点笔记本之js重定位函数

    1. call().bind().apply()的用法,改变this的指向,区别在于f.call(obj, arg1, arg2...),f.bind(obj, arg1, arg2,...)(),f ...

  3. CGTime CMTimeRange CMTimeMapping 小结

    CMTime CMTimeRange CMTimeMapping 在使用 AVFoundation 框架处理多媒体资源时,通常会用到一些在 CoreMedia 框架中定义的结构体, 这里对其中描述时间 ...

  4. 用IDEA查看class字节码反编译后的文件——

    反编译 利用IDEA 进行反编译 查看class字节码反编译后的文件 1.要找到class文件,路径: IDEA没有显示完全,不过你可以点击右边这个,很容易就找到完整的路径. 2.找到这个路径并进入p ...

  5. ES7-ES12总结篇

    脑图模式       插入 ES7-ES12  ES7  Array.prototype.includes()   includes() 方法用来判断一个数组是否包含一个指定的值,如果包含则 ...

  6. element select多选选项卡页面抖动问题

    最近做项目是有个功能需要下拉框多选,然后碰到了一个问题就是选择选项的时候出现频繁抖动的情况 问题描述: 页面选择到三个选项时长度为三的时候就会开始抖动,其他长度没有问题,检索elements是发现选择 ...

  7. nuttx理解

    操作系统:为啥要引入操作系统,个人的理解是为了实时性(即及时的响应性). 没有操作系统下多个任务都只能以前后台的方式排队执行,对某个任务的输入不能得到及时的响应:虽然后台有中断,但不能把所有的任务都放 ...

  8. 搭建sftp服务器

    sftp采用的是ssh加密隧道,安装性方面较ftp强,而且依赖的是系统自带的ssh服务,不像ftp还需要额外的进行安装 1.  创建sftp组 # groupadd sftp   创建完成之后使用ca ...

  9. gulp技术:自动化构建工具

    作用:压缩css.js.img,合并文件,改名字,编译sass,拷贝 使用步骤: 1.安装node环境,下一步,下一步,安装C盘: 2.在你的根目录下,在地址栏输入cmd回车: 3.检测node和np ...

  10. 基于百度智能云api下的车牌识别系统

    车牌识别在高速公路中有着广泛的应用,比如我们常见的电子收费(ETC)系统和交通违章车辆的检测,除此之外像小区或地下车库门禁也会用到,基本上凡是需要对车辆进行身份检测的地方都会用到. 简介 车牌识别系统 ...