博客
关于我
luogu3953 [NOIp2017]逛公园 (tarjan+dijkstra+记忆化搜索)
阅读量:792 次
发布时间:2023-02-06

本文共 3450 字,大约阅读时间需要 11 分钟。

0环问题处理与图的反向搜索方案数计算

问题背景

在处理某些图论问题时,特别是涉及最短路径计算的反向搜索问题时,0环问题可能会出现。0环指的是一个环的起点和终点相同,且环长为0的特殊情况。在这样的情况下,传统的反向搜索方法可能会返回错误的结果,因此需要特殊的处理方法。

方法思路

为了处理0环问题,我们采用以下步骤:

  • Dijkstra算法计算最短路径:首先,我们使用Dijkstra算法从节点1出发,计算出每个节点i到节点1的最短距离dis[i]。

  • 反向搜索处理:接下来,我们从节点N开始进行反向搜索。我们定义一个结构(p, k),其中p表示当前节点,k表示从节点1到p的距离等于dis[p]加上k的方案数。通过这种方式,我们可以统计从节点1到节点N的所有可能路径数。

  • 0环特殊处理:在反向搜索过程中,如果我们发现某个节点p已经被访问过且还在当前的搜索栈中,这意味着存在0环。这种情况下,我们需要返回-1作为错误标记。

  • 方案数累加:最终,我们将所有满足条件的方案数累加,得到最终的答案。

  • 代码实现

    #include 
    using namespace std;typedef long long ll;typedef pair
    pa;const int maxn = 1e5 + 10, maxm = 2e5 + 10, maxk = 55;ll rd() { ll x = 0; char c = getchar(); int neg = 1; while (c < '0' || c > '9') { if (c == '-') neg = -1; c = getchar(); } while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * neg;}struct Edge { int b, l, ne;} eg[maxm], negh[maxm];struct Node { int p, k, n; Node(int a = 0, int b = 0, int c = 0) : p(a), k(b), n(c) {}};int egh[maxn], negh[maxn], ect, nect;int dis[maxn], f[maxn][maxk];int dfn[maxn], tot, low[maxn], stk[maxn], sh;int N, M, K, P;bool in0[maxn], instk[maxn], flag[maxn];void tarjan(int x) { dfn[x] = low[x] = ++tot; instk[x] = 1; stk[++sh] = x; for (int i = egh[x]; i; i = eg[i].ne) { if (eg[i].l) continue; int b = eg[i].b; if (!dfn[b]) tarjan(b), low[x] = min(low[x], low[b]); else if (instk[b]) low[x] = min(low[x], dfn[b]); } if (dfn[x] == low[x]) { int n = 0; for (int i = sh; stk[i] != x; i--) n++; while (1) { instk[stk[sh]] = 0; in0[stk[sh]] = n > 0; if (stk[sh--] == x) break; } }}priority_queue
    , greater
    > q;void dijkstra() { while (!q.empty()) q.pop(); CLR(flag, 0); CLR(dis, 63); dis[1] = 0; q.push(make_pair(0, 1)); while (!q.empty()) { int p = q.top().second; q.pop(); if (flag[p]) continue; flag[p] = 1; for (int i = egh[p]; i; i = eg[i].ne) { int b = eg[i].b; if (dis[b] > dis[p] + eg[i].l) { dis[b] = dis[p] + eg[i].l; q.push(make_pair(dis[b], b)); } } }}inline int solve(int x, int y) { if (y < 0 || y > K) return 0; if (in0[x]) return -1; if (f[x][y] >= 0) return f[x][y]; f[x][y] = 0; for (int i = negh[x]; i; i = neg[i].ne) { int b = neg[i].b; int re = solve(b, dis[x] - dis[b] + y - neg[i].l); if (re == -1) return -1; f[x][y] += re; f[x][y] %= P; } return f[x][y];}int main() { int i, j, k; for (int T = rd(); T; T--) { N = rd(), M = rd(), K = rd(), P = rd(); CLR(egh, 0); ect = 0; CLR(negh, 0); nect = 0; for (i = 1; i <= M; ++i) { int a = rd(), b = rd(), c = rd(); adeg(a, b, c); } CLR(dfn, 0); CLR(instk, 0); tot = 0; CLR(in0, 0); for (i = 1; i <= N; ++i) { if (!dfn[i]) tarjan(i); } dijkstra(); CLR(f, -1); f[1][0] = 1; int ans = 0; for (i = 0; i <= K; ++i) { ans += solve(N, i); ans %= P; } printf("%d\n", ans < 0 ? -1 : ans); } return 0;}

    代码解释

  • 读取输入:使用rd()函数读取输入数据,解析图的节点和边信息。
  • Dijkstra算法:计算从节点1到所有其他节点的最短距离。
  • Tarjan算法:用于检测0环问题,标记涉及0环的节点。
  • 反向搜索:从节点N开始,统计从节点1到节点N的所有可能路径数。
  • 方案数计算:累加所有符合条件的方案数,返回结果。
  • 该方法通过Dijkstra算法和Tarjan算法的结合,有效解决了0环问题,并正确计算了反向搜索的方案数。

    转载地址:http://gvufk.baihongyu.com/

    你可能感兴趣的文章
    list深拷贝和浅拷贝
    查看>>
    List集合排序找出其中的最大和最小值
    查看>>
    list<Map> 怎么转list<String>
    查看>>
    2025年04月10日IT技术领域重点关注焦点
    查看>>
    List<String>用空串替换null值,并且都加上单引号,并且转为字符串用,分割
    查看>>
    liunx 下WebBench 安装与压力测试
    查看>>
    Liunx 多命令/管道符/wc命令/man命令汉化
    查看>>
    liunx 服务内存消耗100% 怎么处理
    查看>>
    liunx 网络基础管理
    查看>>
    liunx-FTP服务器_无需整理
    查看>>
    liunx上安装MySQL没有默认my.cnf文件解决方案
    查看>>
    Liunx中各种压缩包及解压命令
    查看>>
    liunx命令查看cpu使用率和负载情况
    查看>>
    liunx快速修改文件夹或文件的属性
    查看>>
    Liunx挂载nfts盘数据方法
    查看>>
    liunx查找当前目录文件及子目录文件下的中文并替换
    查看>>
    liunx目录和文件管理(一)
    查看>>
    liunx系统中的文件压缩与解压
    查看>>
    liunx编写启动,kill进程脚本
    查看>>
    liux的学习笔记
    查看>>