说明

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

但是在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. 作业一:PCA降维练习

    作业一:PCA降维作业 代码 点击查看代码 #author:qiao_px #@Time 2022/10/31 16:11 #@File ceshiPCA.py import pandas as pd ...

  2. 在Unity3D中开发的Toon Shader

    SwordMaster Toon Shader 特点 此卡通渲染风格的Shader是顶点片元Shader,由本人手动编写完成 此卡通渲染风格的Shader已经在移动设备真机上进行过测试,可以直接应用到 ...

  3. Redis Stream实现全部节点机器推送消息

    背景 有时候,在微服务时代,我们需要对全部的机器节点进行通知.在常规情况下,一个请求经过负载均衡只有一个机器可以收到.那么,如何能让全部的机器都收到同样的请求呢?需要借助消息队列的监听机制,让每个节点 ...

  4. iOS 制作本地库Development Pods

    1.cd到指定的路径文件夹下 ,创建本地库,如:pod lib create XXNetworking 2.删除Classes文件下的ReplaceMe文件,添加自己的类. 3.修改XXNetwork ...

  5. 西瓜书3.4 解题报告(python 多分类学习 十折交叉法)

    偷懒找了UCI上最小的一个数据集,数据大约是集装箱起重机的转动速度.角度,判断其力量大小(我不懂起重机啊啊啊) 虽然不懂但并不妨碍写代码分类,显然标记就是力量,分为0.3.0.5.0.7三种.具体的模 ...

  6. PTA最大子列和问题

    给定K个整数组成的序列{ N1​, N2​, ..., NK​ },"连续子列"被定义为{ Ni​, Ni+1​, ..., Nj​ },其中 1≤i≤j≤K."最大子列 ...

  7. HashMap记录

    1.HashMap接收null的键值 2.HashMap是非synchronized的 3.HashMap使用hashCode找到bucket的位置.bucket中存储的是键和值 4.当HashCod ...

  8. CentOS 7(Linux)安装docker,执行yum install docker-io -y报错

    CentOS 7(Linux)安装Docker,执行yum install docker-io -y报错 一.执行yum install docker-io -y报错 Error: docker-ce ...

  9. MSSQL SQL SERVER 2008 使用RowNumber()分页查询并获取总行数 附达梦数据库

    参数:pages:要查询的页码(要查询第几页):pageNum:要查询的行数(每页要查多少行):适用于使用多表查询,不以固定的实体类保存结果,如使用 List<Map<String, Ob ...

  10. Blob文件下载type类型

    let url = window.URL.createObjectURL(new Blob([文件流(一般为res.data)], {type: "Blob类型"}) let li ...