LuoguP3953 逛公园(最短路+dp)

逛公园

题目:

策策同学特别喜欢逛公园。公园可以看成一张 N 个点 M 条边构成的有向图,且没有 自环和重边。其中 1 号点是公园的入口,N 号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间。
策策每天都会去逛公园,他总是从 1 号点进去,从 N 号点出来。策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间。如果 1 号点 到 N 号点的最短路长为 d,那么策策只会喜欢长度不超过 d+K 的路线。
策策同学想知道总共有多少条满足条件的路线,你能帮帮它吗?为避免输出过大,答案对 P 取模。如果有无穷多条合法的路线,请输出 1

思路:

因为题目保证了从 1N 一定是可以到达的,而且同时还要知道最短路的长 d ,就不难想到要跑一遍最短路,但是我们要知道其他能够到达 n 的方案的路径长度,所以连边的时候不能够用无向边来存储,需要建两个图(正图,反图), 正图用来跑出来 1N 的最短路的长度,反图用来处理相差不超过 K 的方案。

    int n, m, k, p;
    std::cin >> n >> m >> k >> p;
    //建出正反图
    std::vector<std::vector<std::array<int, 2>>> g1(n + 1);
    std::vector<std::vector<std::array<int, 2>>> g2(n + 1);
    for (int i = 0; i < m; i++) {
        int u, v, w;
        std::cin >> u >> v >> w;
        g1[u].push_back({v, w});
        g2[v].push_back({u, w});
    }

    // Dijkstra算法
    std::priority_queue<std::array<int, 2>, std::vector<std::array<int, 2>>, std::greater<std::array<int, 2>>> q;
    q.push({0, 1});
    std::vector<int> dist(n + 1, 1E9);
    std::vector<bool> vis(n + 1);
    dist[1] = 0;
    while(q.size()) {
        auto [w, u] = q.top(); q.pop();
        if (vis[u]) continue;
        vis[u] = true;

        for (auto& [v, ww] : g1[u]) {
            if (dist[v] > dist[u] + ww) {
                dist[v] = dist[u] + ww;
                q.push({dist[v], v});
            }
        }
    }

由于最终要求的是方案数,并且同样有相差不超过 K 的额外限制,所以考虑用 dp 来求解方案数。定义状态为 dp[u][k] 代表从 1u 的路径中长度为 distu+k 的方案数。现在考虑状态的转移,假设现在要从 uv ,也就是 dp[u][k]dp[v][x] 也就是 distv+x+w(u,v)=distu+kx=distudistv+kw(u,v) 。这样 dp[u][k] 就可以从 dp[v][distudistv+kw(u,v)] 转移过来。在反图上跑一遍 dp ,用记忆化搜索的方式会比较好写,

    std::vector<std::vector<bool>> st(n + 1, std::vector<bool> (k + 1));
    std::vector<std::vector<int>> dp(n + 1, std::vector<int> (k + 1));

    int f = 0;
    std::function<int(int, int)> dfs = [&](int u, int now) -> int {
        if (now < 0) return 0;
        if (st[u][now]) { f = 1; return 0; }
        if (dp[u][now]) return dp[u][now];
        st[u][now] = true;

        int ans = 0;
        for (auto& [v, w] : g2[u]) {
            ans = (ans + dfs(v, dist[u] - dist[v] + now - w)) % p;
            if (f) return 0;
        }
        st[u][now] = false;
        return dp[u][now] = ans;
    };

    dfs(1, 0);
    dp[1][0] = 1;
    int ans = 0;
    for (int i = 0; i <= k; i++) {
        ans = (ans + dfs(n, i)) % p;
    }

    if (f == 1) return void(std::cout << "-1\n");
    std::cout << ans << "\n";
posted @   浅渊  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示