comments | difficulty | edit_url | tags | ||
---|---|---|---|---|---|
true |
中等 |
|
在一个火车旅行很受欢迎的国度,你提前一年计划了一些火车旅行。在接下来的一年里,你要旅行的日子将以一个名为 days
的数组给出。每一项是一个从 1
到 365
的整数。
火车票有 三种不同的销售方式 :
- 一张 为期一天 的通行证售价为
costs[0]
美元; - 一张 为期七天 的通行证售价为
costs[1]
美元; - 一张 为期三十天 的通行证售价为
costs[2]
美元。
通行证允许数天无限制的旅行。 例如,如果我们在第 2
天获得一张 为期 7 天 的通行证,那么我们可以连着旅行 7 天:第 2
天、第 3
天、第 4
天、第 5
天、第 6
天、第 7
天和第 8
天。
返回 你想要完成在给定的列表 days
中列出的每一天的旅行所需要的最低消费 。
示例 1:
输入:days = [1,4,6,7,8,20], costs = [2,7,15] 输出:11 解释: 例如,这里有一种购买通行证的方法,可以让你完成你的旅行计划: 在第 1 天,你花了 costs[0] = $2 买了一张为期 1 天的通行证,它将在第 1 天生效。 在第 3 天,你花了 costs[1] = $7 买了一张为期 7 天的通行证,它将在第 3, 4, ..., 9 天生效。 在第 20 天,你花了 costs[0] = $2 买了一张为期 1 天的通行证,它将在第 20 天生效。 你总共花了 $11,并完成了你计划的每一天旅行。
示例 2:
输入:days = [1,2,3,4,5,6,7,8,9,10,30,31], costs = [2,7,15] 输出:17 解释: 例如,这里有一种购买通行证的方法,可以让你完成你的旅行计划: 在第 1 天,你花了 costs[2] = $15 买了一张为期 30 天的通行证,它将在第 1, 2, ..., 30 天生效。 在第 31 天,你花了 costs[0] = $2 买了一张为期 1 天的通行证,它将在第 31 天生效。 你总共花了 $17,并完成了你计划的每一天旅行。
提示:
1 <= days.length <= 365
1 <= days[i] <= 365
days
按顺序严格递增costs.length == 3
1 <= costs[i] <= 1000
我们定义一个函数
函数
- 如果
$i \geq n$ ,表示所有出行已经结束,返回$0$ ; - 否则,我们需要考虑三种购买方式,分别是购买
$1$ 天通行证、购买$7$ 天通行证和购买$30$ 天通行证。我们分别计算这三种购买方式的花费,并且利用二分查找,找到下一次出行的下标$j$ ,然后递归调用$\textit{dfs(j)}$ ,最后返回这三种购买方式的最小花费。
为了避免重复计算,我们使用记忆化搜索,将已经计算过的结果保存起来。
时间复杂度
class Solution:
def mincostTickets(self, days: List[int], costs: List[int]) -> int:
@cache
def dfs(i: int) -> int:
if i >= n:
return 0
ans = inf
for c, v in zip(costs, valid):
j = bisect_left(days, days[i] + v)
ans = min(ans, c + dfs(j))
return ans
n = len(days)
valid = [1, 7, 30]
return dfs(0)
class Solution {
private final int[] valid = {1, 7, 30};
private int[] days;
private int[] costs;
private Integer[] f;
private int n;
public int mincostTickets(int[] days, int[] costs) {
n = days.length;
f = new Integer[n];
this.days = days;
this.costs = costs;
return dfs(0);
}
private int dfs(int i) {
if (i >= n) {
return 0;
}
if (f[i] != null) {
return f[i];
}
f[i] = Integer.MAX_VALUE;
for (int k = 0; k < 3; ++k) {
int j = Arrays.binarySearch(days, days[i] + valid[k]);
j = j < 0 ? -j - 1 : j;
f[i] = Math.min(f[i], dfs(j) + costs[k]);
}
return f[i];
}
}
class Solution {
public:
int mincostTickets(vector<int>& days, vector<int>& costs) {
int valid[3] = {1, 7, 30};
int n = days.size();
int f[n];
memset(f, 0, sizeof(f));
function<int(int)> dfs = [&](int i) {
if (i >= n) {
return 0;
}
if (f[i]) {
return f[i];
}
f[i] = INT_MAX;
for (int k = 0; k < 3; ++k) {
int j = lower_bound(days.begin(), days.end(), days[i] + valid[k]) - days.begin();
f[i] = min(f[i], dfs(j) + costs[k]);
}
return f[i];
};
return dfs(0);
}
};
func mincostTickets(days []int, costs []int) int {
valid := [3]int{1, 7, 30}
n := len(days)
f := make([]int, n)
var dfs func(int) int
dfs = func(i int) int {
if i >= n {
return 0
}
if f[i] > 0 {
return f[i]
}
f[i] = 1 << 30
for k := 0; k < 3; k++ {
j := sort.SearchInts(days, days[i]+valid[k])
f[i] = min(f[i], dfs(j)+costs[k])
}
return f[i]
}
return dfs(0)
}
function mincostTickets(days: number[], costs: number[]): number {
const n = days.length;
const f: number[] = Array(n).fill(0);
const valid: number[] = [1, 7, 30];
const search = (x: number): number => {
let [l, r] = [0, n];
while (l < r) {
const mid = (l + r) >> 1;
if (days[mid] >= x) {
r = mid;
} else {
l = mid + 1;
}
}
return l;
};
const dfs = (i: number): number => {
if (i >= n) {
return 0;
}
if (f[i]) {
return f[i];
}
f[i] = Infinity;
for (let k = 0; k < 3; ++k) {
const j = search(days[i] + valid[k]);
f[i] = Math.min(f[i], dfs(j) + costs[k]);
}
return f[i];
};
return dfs(0);
}
我们不妨记
我们可以按照
最终答案为
时间复杂度
class Solution:
def mincostTickets(self, days: List[int], costs: List[int]) -> int:
m = days[-1]
f = [0] * (m + 1)
valid = [1, 7, 30]
j = 0
for i in range(1, m + 1):
if i == days[j]:
f[i] = inf
for c, v in zip(costs, valid):
f[i] = min(f[i], f[max(0, i - v)] + c)
j += 1
else:
f[i] = f[i - 1]
return f[m]
class Solution {
public int mincostTickets(int[] days, int[] costs) {
int m = days[days.length - 1];
int[] f = new int[m + 1];
final int[] valid = {1, 7, 30};
for (int i = 1, j = 0; i <= m; ++i) {
if (i == days[j]) {
f[i] = Integer.MAX_VALUE;
for (int k = 0; k < 3; ++k) {
int c = costs[k], v = valid[k];
f[i] = Math.min(f[i], f[Math.max(0, i - v)] + c);
}
++j;
} else {
f[i] = f[i - 1];
}
}
return f[m];
}
}
class Solution {
public:
int mincostTickets(vector<int>& days, vector<int>& costs) {
int m = days.back();
int f[m + 1];
f[0] = 0;
int valid[3] = {1, 7, 30};
for (int i = 1, j = 0; i <= m; ++i) {
if (i == days[j]) {
f[i] = INT_MAX;
for (int k = 0; k < 3; ++k) {
int c = costs[k], v = valid[k];
f[i] = min(f[i], f[max(0, i - v)] + c);
}
++j;
} else {
f[i] = f[i - 1];
}
}
return f[m];
}
};
func mincostTickets(days []int, costs []int) int {
m := days[len(days)-1]
f := make([]int, m+1)
valid := [3]int{1, 7, 30}
for i, j := 1, 0; i <= m; i++ {
if i == days[j] {
f[i] = 1 << 30
for k, v := range valid {
c := costs[k]
f[i] = min(f[i], f[max(0, i-v)]+c)
}
j++
} else {
f[i] = f[i-1]
}
}
return f[m]
}
function mincostTickets(days: number[], costs: number[]): number {
const m = days.at(-1)!;
const f: number[] = Array(m).fill(0);
const valid: number[] = [1, 7, 30];
for (let i = 1, j = 0; i <= m; ++i) {
if (i === days[j]) {
f[i] = Infinity;
for (let k = 0; k < 3; ++k) {
const [c, v] = [costs[k], valid[k]];
f[i] = Math.min(f[i], f[Math.max(0, i - v)] + c);
}
++j;
} else {
f[i] = f[i - 1];
}
}
return f[m];
}