本文主要是介绍983. 最低票价-M,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
983. 最低票价-M
label: dp不连续,滑动窗口
在一个火车旅行很受欢迎的国度,你提前一年计划了一些火车旅行。在接下来的一年里,你要旅行的日子将以一个名为 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
分析:这就找01背包问题类似,只不过这里days是断断续续,如果是连续的那直接dp就好办,断续就得用几个循环进行,不要以为这很烂,就是这种解法,重点在于处理“断“的地方。
- 滑动窗口,[i,j],想后看7和30天并更新票钱
- 使用连续的days方法,就和硬币一样,但是需要留意的是,断的地方要直接复制前边存在的那天,这样才能dp[i-7],dp[i-30]的方式进行
- 类似方法2,但是当时考虑不足,打了好几个补丁,比如没有处理好断的地方
需要注意的是,costs不是递增的,比如case: costs{7,2,15}
滑动窗口,并向后看:
package main
import "fmt"
/*
执行用时 :4 ms, 在所有 Go 提交中击败了66.67%的用户
内存消耗 :2.3 MB, 在所有 Go 提交中击败了100.00%的用户
*/
func mincostTickets(days []int, costs []int) int {dp:=make([]int,len(days)+1)for i:=0;i<len(days);i++{if dp[i+1]>dp[i]+costs[0]||dp[i+1]==0{dp[i+1]=dp[i]+costs[0]//后看1天}//滑动窗口,就是要在一个范围内进行来回比较for j:=i+1;j<=i+7&&j<=len(days)&&days[j-1]<days[i]+7;j++{if dp[j]>dp[i]+costs[1]||dp[j]==0{dp[j]=dp[i]+costs[1]}}for j:=i+1;j<=i+30&&j<=len(days)&&days[j-1]<days[i]+30;j++{if dp[j]>dp[i]+costs[2]||dp[j]==0{dp[j]=dp[i]+costs[2]}}}return dp[len(days)]
}
func main() {talbes:=[][]int{{1,4,6,7,8,20},{2,7,15},//11{1,2,3,4,5,6,7,8,9,10,30,31},{2,7,15},//17{1,4,6,7,8,20},{7,2,15},//6{1,2,3,4,6,8,9,10,13,14,16,17,19,21,24,26,27,28,29},{3,14,50},//50}for i:=0;i<len(talbes);i+=2{fmt.Println(mincostTickets(talbes[i],talbes[i+1]))}
}
使用连续的办法,向前看:
/*
执行用时 :4 ms, 在所有 Go 提交中击败了66.67%的用户
内存消耗 :2.8 MB, 在所有 Go 提交中击败了100.00%的用户
*/
func mincostTickets(days []int, costs []int) int {dp:=make([]int,days[len(days)-1]+1)mp:=make(map[int]int)for i:=0;i<len(days);i++{mp[days[i]]=1}for i:=1;i<=days[len(days)-1];i++{//不在则copyif _,ok:=mp[i];!ok{dp[i]=dp[i-1] //断的部分处理continue}//在则更新计算 dp[i]=dp[i-1]+costs[0] //旅游日,分别向前看1,7,30天的价钱,pre:=0if i>=7{pre=i-7}//dp[i]表示0到i花费的最少价钱,都是前开后闭的区间形式if dp[pre]+costs[1]<dp[i]{dp[i]=dp[pre]+costs[1]}pre=0if i>=30{pre=i-30}if dp[pre]+costs[2]<dp[i]{dp[i]=dp[pre]+costs[2]}}return dp[days[len(days)-1]]
}
初次通过,打了几个补丁,就比较恶心了,记录这个丑陋的代码
/*
执行用时 :4 ms, 在所有 Go 提交中击败了66.67%的用户
内存消耗 :2.8 MB, 在所有 Go 提交中击败了100.00%的用户
*/
func mincostTickets2(days []int, costs []int) int {dp:=make([]int,days[len(days)-1]+1)dp[days[0]]=costs[0]for i:=1;i<len(days);i++{dp[days[i]]=dp[days[i-1]]+costs[0]}mp:=make(map[int]int)for i:=0;i<len(days);i++{mp[days[i]]=1}for i:=1;i<=days[len(days)-1];i++{if _,ok:=mp[i];!ok{dp[i]=dp[i-1]}else{if dp[i]==0{dp[i]=costs[0]}}if dp[i-1]+costs[0]<dp[i]{dp[i]=dp[i-1]+costs[0]}if i>=7 {if dp[i-7]+costs[1] < dp[i]{dp[i] = dp[i-7] + costs[1]}}else {if dp[0]+costs[1]<dp[i] {dp[i] = dp[0]+costs[1]}}if i>=30{if dp[i-30]+costs[2]<dp[i]{dp[i]=dp[i-30]+costs[2]}}else{//中间断的部分,也可以利用if dp[0]+costs[2]<dp[i]{dp[i]=dp[0]+costs[2]}}}return dp[days[len(days)-1]]
}
这篇关于983. 最低票价-M的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!