【线性DP】AcWing 4518. 最低票价
题目
https://www.acwing.com/problem/content/description/4521/
题解
对于该题,必须购买到能覆盖整个 \(days\) 数组的票。不妨假设 \(days = [x, y, z]\),很明显无论哪一种方案,从第 \(x\) 天开始购票都会是比较好的方案,因为在第 \(1\) ~ \((x - 1)\) 天不需要旅游,所以这些日期不需要购票。
维护一个大小为 \(n\) 的 \(dp\) 数组,\(dp[i]\) 代表到第 \(days[i]\) 天为止的最小开销,初始化为无穷大。
设在第 \(x\) 天,第 \(y\) 天,第 \(z\) 天的开销分别是 \(cost_x, cost_y, cost_z\)。此时,再思考一下 \(cost_x, cost_y, cost_z\) 的大小关系?
很明显必定会满足以下关系:\(cost_x <= cost_y <= cost_z\)。
既然存在上述大小关系,说明靠后的日期的开销必定大于等于靠前的日期的开销,所以从第 \(days[i]\) 天购买一张票价为 \(cost[j]\) 能够旅游 \(p\) 天的票,假设满足 \(days[k] \geq days[i] + p - 1\) 的最大下标为 \(k\),那么只需要维护 \(dp[k] = min(dp[k], dp[i - 1] + cost[j])\) 即可。
参考代码
#include<bits/stdc++.h>
using namespace std;
constexpr int INF = 0x3f3f3f3f;
int days[366], dp[366], costs[3], steps[3] = {0, 6, 29};
int n;
int main() {
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
cin >> n;
fill(dp + 1, dp + 1 + n, INF);// 初始化每一天的开销都是无穷大
for (int i = 1; i <= n; ++ i) cin >> days[i];// 输入日期数组
for (int i = 0; i < 3; ++ i) cin >> costs[i];// 输入三种类型的票的售价
for (int i = 1; i <= n; ++ i) {// 遍历每一个日期
for (int j = 0; j < 3; ++ j) {// 遍历每一种票的类型
// 在第 days[i] 天,购买售价为 cost[j] 的可旅行到第 days[i] + steps[j] 天为止的票
int k = upper_bound(days + i, days + 1 + n, days[i] + steps[j]) - days - 1;// 计算出能旅游到的最后一天的下标
dp[k] = min(dp[k], dp[i - 1] + costs[j]);// 计算出这一天的最小开销
}
}
cout << dp[n] << '\n';
return 0;
}
浙公网安备 33010602011771号