说明

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

但是在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. 20 local_settings文件配置 && .gitignore

    1 local_settings 第一步:需要在项目根目录下的settings配置如下 try: from .local_settings import * except Exception: pas ...

  2. pycharm怎么查看某个包pycharm怎么查看某个包的源码的源码

    安装ctrl点击对应的函数名或是包名即可

  3. 显式等待(一)WebDriverWait类、until()方法

    转自: https://blog.csdn.net/zyooooxie/article/details/84422924

  4. Centos7 服务器安装jdk

    给自己留个步骤  有些centos系统会自带openjdk,我们需要先卸载 首先输入java -version,验证是否有安装jdk 如果有,接着输入rpm -qa | grep java 来显示jd ...

  5. SQLite 帮助类

    public static class SqliteHelper { /// <summary> /// 获得连接对象 /// </summary> /// <retur ...

  6. C# 使用Enumerable.Range 打印数字

    static void Main(string[] args) { var list1 = Enumerable.Range(0, (int)Math.Pow(2, 22)).ToList(); va ...

  7. Floyd算法求最短路径

    floyd算法用于求图中各个点到其它点的最短路径,无论其中经过多少个中间点.该算法的核心理念是基于动态规划, 不断更新最短距离,遍历所有的点. 知识基础:图的邻接矩阵表示: 如图是一个简单图,从A开始 ...

  8. 5-3 掌握 egg.js + 云 mongodb

    1 egg.js 1.1 初始化 初始化和项目启动方法 # 初始化 $ mkdir egg-example && cd egg-example $ npm init egg --typ ...

  9. Java8中Stream的用法

    Java8中Stream的用法 1.概述 Stream APl ( java.util.stream)把真正的函数式编程风格引入到Java中.这是目前为止对Java类库最好的补充,因为Stream A ...

  10. jsp第4个作业(2)

    <%@ page language="java" import="java.util.*" pageEncoding="utf-8"% ...