序列dp

题目大意

个点 条边,每一条边都是双向边,第 条道路连接着 ,长度为 。现在有一个长度为 的序列,第 个数为 。接下来你需要在这个序列当中选择若干个数,使得这些数对应的边连接起一条 的路径,问你这一条路径的最小长度。如果没有,输出

思路

看到在 个数当中选择若干个数,我们果断选择 dp。我们设 表示为选择了前 个数对应的边, 的最小距离。那么对于第 条边,我们可以选择选与不选,因为对后面的决策毫无影响,在其中取最小值就行了:

接着,我们发现了 数组内存占用过大,而且每一次转移时,首先就需要将上一层的状态全部拷贝下来,而根本就没有用到本层状态,因此我们可以压数组,把 这个维度给压掉,这样子就可以愉快地通过此题了。时间复杂度:,空间复杂度

代码

#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;
}