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";

__EOF__

本文作者HoneyGrey
本文链接https://www.cnblogs.com/Haven-/p/16794433.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   浅渊  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示