LuoguP3953 逛公园(最短路+dp)
逛公园
题目:
策策同学特别喜欢逛公园。公园可以看成一张 \(N\) 个点 \(M\) 条边构成的有向图,且没有 自环和重边。其中 \(1\) 号点是公园的入口,\(N\) 号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间。
策策每天都会去逛公园,他总是从 \(1\) 号点进去,从 \(N\) 号点出来。策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间。如果 \(1\) 号点 到 \(N\) 号点的最短路长为 \(d\),那么策策只会喜欢长度不超过 \(d + K\) 的路线。
策策同学想知道总共有多少条满足条件的路线,你能帮帮它吗?为避免输出过大,答案对 \(P\) 取模。如果有无穷多条合法的路线,请输出 \(-1\)。
思路:
因为题目保证了从 \(1\) 到 \(N\) 一定是可以到达的,而且同时还要知道最短路的长 \(d\) ,就不难想到要跑一遍最短路,但是我们要知道其他能够到达 \(n\) 的方案的路径长度,所以连边的时候不能够用无向边来存储,需要建两个图(正图,反图), 正图用来跑出来 \(1 \rightarrow N\) 的最短路的长度,反图用来处理相差不超过 \(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]\) 代表从 \(1 \rightarrow u\) 的路径中长度为 \(dist_u + k\) 的方案数。现在考虑状态的转移,假设现在要从 \(u \rightarrow v\) ,也就是 \(dp[u][k] \rightarrow dp[v][x]\) 也就是 \(dist_v + x + w(u, v) = dist_u + k \Rightarrow x = dist_u - dist_v + k - w(u, v)\) 。这样 \(dp[u][k]\) 就可以从 \(dp[v][dist_u - dist_v + k - w(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";

浙公网安备 33010602011771号