[ABC271E] Subsequence Path 题解
题目大意
有 \(N\) 个点 \(M\) 条边,每一条边都是双向边,第 \(i\) 条道路连接着 \(A_i\) 和 \(B_i\),长度为 \(C_i\)。现在有一个长度为 \(K\) 的序列,第 \(i\) 个数为 \(E_i\) \((1\le E_i\le M)\)。接下来你需要在这个序列当中选择若干个数,使得这些数对应的边连接起一条 \(1\) 到 \(N\) 的路径,问你这一条路径的最小长度。如果没有,输出 \(-1\)。
思路
看到在 \(K\) 个数当中选择若干个数,我们果断选择 dp。我们设 \(dp_{(i,j)}\) 表示为选择了前 \(i\) 个数对应的边,\(1\) 到 \(j\) 的最小距离。那么对于第 \(i\) 条边,我们可以选择选与不选,因为对后面的决策毫无影响,在其中取最小值就行了:
\[dp_{(i,B_{(E_i)})}=\min(dp_{(i-1,B_{(E_i)})},dp_{(i-1,a_{(A_i)})}+C_{(E_{i})})
\]
接着,我们发现了 \(dp\) 数组内存占用过大,而且每一次转移时,首先就需要将上一层的状态全部拷贝下来,而根本就没有用到本层状态,因此我们可以压数组,把 \(i\) 这个维度给压掉,这样子就可以愉快地通过此题了。时间复杂度:\(\mathcal O(\max(M,K))\),空间复杂度 \(\mathcal O(N)\)。
代码
#include <iostream>
using namespace std;
using ll = long long; // 10^9 累加会爆 int
const ll kMaxN = 2e5 + 5; // 最大边数和点数
ll a[kMaxN], b[kMaxN], c[kMaxN], e[kMaxN], dp[kMaxN], n, m, k;
int main() {
cin >> n >> m >> k;
for (ll i = 1; i <= m; i++) {
cin >> a[i] >> b[i] >> c[i];
}
for (ll i = 1; i <= k; i++) {
cin >> e[i];
}
fill(dp + 2, dp + n + 1, 1e18); // 初始化 dp 数组,注意不初始化 dp[1],dp[1] 仍为 0
for (ll i = 1; i <= k; i++) { // 枚举第 i 个数
dp[b[e[i]]] = min(dp[b[e[i]]], dp[a[e[i]]] + c[e[i]]); // 不选和选取最小值
}
cout << (dp[n] == 1e18 ? -1 : dp[n]) << '\n'; // 判断是否能够到达并输出
return 0;
}

浙公网安备 33010602011771号