【Luogu】每日一题——Day15. P1144 最短路计数 (记忆化搜索 + 图论 + 最短路)

链接:P1144 最短路计数 - 洛谷

题目:

思路:

统计答案比较细节

题目很简单,我们只需要先求出每个点的最短距离,然后用一次记忆化搜索即可

具体的,先使用 SPFA 求出点 1 到 每个点 的最短距离,然后我们采取记忆化搜索,对于点 1,其答案初始化为 1,对于所有子节点,我们利用记忆化搜索枚举答案,对于一个点的子节点 u,如果满足 disu = disfa - 1,说明我们的 fa 可以从 u 转移过来,此时继续递归 u 即可

由于这一题是无权图,所以所有边权为 1 即可

代码:

cpp 复制代码
#include <iostream>
#include <algorithm>
#include<cstring>
#include <iomanip>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <utility>
#include <array>
#include <tuple>
using namespace std;
#define int long long
#define yes cout << "YES" << endl
#define no cout << "NO" << endl
const int N = 1e6 + 5;
const int MOD = 100003;
int n, m;
int dis[N];
int vis[N];
int dp[N];
vector<vector<int>> g(N);

int dfs(int fa)
{
    if (dp[fa])
    {
        return dp[fa];
    }
    for(auto & to : g[fa])
    {
        if (dis[to] == dis[fa] - 1)
        {
            dp[fa] = (dp[fa] + dfs(to)) % MOD;
        }
    }
    return dp[fa];
}

void solve()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        dis[i] = 1e18;
    }
    for (int i = 0; i < m; i++)
    {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    queue<int> q;
    dis[1] = 0;
    q.push(1);
    vis[1] = 1;
    while (!q.empty())
    {
        auto t = q.front();
        q.pop();
        vis[t] = 0;
        for (auto & to : g[t])
        {
            if (dis[to] > dis[t] + 1)
            {
                dis[to] = dis[t] + 1;
                if (!vis[to])
                {
                    vis[to] = 1;
                    q.push(to);
                }
            }
        }
    }
    dp[1] = 1;
    for (int i = 1; i <= n; i++)
    {
        cout << dfs(i) << endl;
    }
}
signed main()
{
    cin.tie(0)->sync_with_stdio(false);
    int t = 1;
    //cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

拓展:

若对于有权图,那么我们则还需要在边上加上 w 这个元素

如:

对于这一题我们还是一样的讨论,只不过到了 1 变成了 w,这里我们主要讨论什么时候能无穷多种走法,注意到题目种 w 可能为 0,此时我们可以反复走过这条边来刷步数,即当 1 -> n 的这条路上含有 边权为 0 的边时有无穷多种走法

cpp 复制代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <iomanip>
#include <cctype>
#include <string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <utility>
#include <array>
#include <tuple>
using namespace std;
#define int long long
#define yes cout << "YES" << endl
#define no cout << "NO" << endl
const int N = 1e6 + 5;
const int MOD = 1000000009;
int n, m;
int dis[N];
int vis[N];
int dp[N];
int flag = 0;
vector<vector<pair<int, int>>> g(N);

int dfs(int fa)
{
    if (flag)
    {
        return -1;
    }
    if (dp[fa])
    {
        return dp[fa];
    }
    for (auto &son : g[fa])
    {
        if (flag)
        {
            return -1;
        }
        int to = son.first;
        int w = son.second;
        if (dis[to] == dis[fa] - w)
        {
            if (w == 0)
            {
                flag = 1;
                return -1;
            }
            dp[fa] = (dp[fa] + dfs(to)) % MOD;
        }
    }
    return dp[fa];
}

void solve()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        dis[i] = 1e18;
    }
    for (int i = 0; i < m; i++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        g[u].push_back({v, w});
        g[v].push_back({u, w});
    }
    queue<int> q;
    dis[1] = 0;
    q.push(1);
    vis[1] = 1;
    while (!q.empty())
    {
        auto t = q.front();
        q.pop();
        vis[t] = 0;
        for (auto &son : g[t])
        {
            int to = son.first;
            int w = son.second;
            if (dis[to] > dis[t] + w)
            {
                dis[to] = dis[t] + w;
                if (!vis[to])
                {
                    vis[to] = 1;
                    q.push(to);
                }
            }
        }
    }
    dp[1] = 1;
    // for (int i = 1; i <= n; i++)
    // {
    //     cout << dfs(i) << endl;
    // }
    int ans = dfs(n);
    cout << (!flag ? ans : -1) << endl;
}
signed main()
{
    cin.tie(0)->sync_with_stdio(false);
    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}
相关推荐
Frostnova丶13 小时前
(19)LeetCode 54. 螺旋矩阵
算法·leetcode·矩阵
Frostnova丶13 小时前
(18)LeetCode 73. 矩阵置零
算法·leetcode·矩阵
冻柠檬飞冰走茶13 小时前
PTA基础编程题目集 7-11 分段计算居民水费(C语言实现)
c语言·开发语言·算法
得物技术14 小时前
推荐系统体验的数字化突破:得物自动化评测平台的技术实践|AICon 文章整理
算法·架构·llm
VALENIAN瓦伦尼安教学设备14 小时前
转子/行星/平行轴齿轮箱综合故障模拟实验台可复现常见机械问题
大数据·数据库·人工智能·嵌入式硬件·算法
rannn_11114 小时前
【力扣hot100】哈希表专题——从两数之和到最长连续序列
java·算法·leetcode·哈希
小保CPP14 小时前
OpenCV C++基于极值区域滤波算法的场景文本检测(OCR)
c++·人工智能·opencv·算法·计算机视觉·ocr
来一碗刘肉面15 小时前
字符串模式匹配(朴素模式匹配算法与KMP算法)
数据结构·算法
dyxal15 小时前
eˣ 的泰勒展开式:从“化繁为简”到“万物统一”的微积分之美
算法
BetterFlow_CFD15 小时前
COMSOL声学仿真基础知识(二)
开发语言·算法·matlab