题目描述
Wonderland是小王居住地一家很受欢迎的游乐园。Wonderland目前有4种售票方式,分别为一日票(1天)、三日票(3天)、周票(7天)和月票(30天)。
每种售票方式的价格由一个数组给出,每种票据在票面时限内可以无限制地进行游玩。例如:
小王在第10日买了一张三日票,小王可以在第10日、第11日和第12日进行无限制地游玩。
小王计划在接下来一年多次游玩该游乐园。小王计划地游玩日期将由一个数组给出。
现在,请您根据给出地售票价格数组和小王计划游玩日期数组,返回游玩计划所需要地最低消费。
输入描述
输入为2个数组:
- 售票价格数组为costs,costs.length = 4,默认顺序为一日票、三日票、周票和月票。
- 小王计划游玩日期数组为days,1 ≤ days.length ≤ 365,1 ≤ days[i] ≤ 365,默认顺序为升序。
输出描述
完成游玩计划的最低消费。
用例
输入 | 5 14 30 100 1 3 5 20 21 200 202 230 |
输出 | 40 |
说明 | 根据售票价格数组和游玩日期数组给出的信息,发现每次去玩的时候买一张一日票是最省钱的,所以小王会卖8张一日票,每张5元,最低花费是40元。 |
题目解析
本题可以使用动态规划求解。
定义一个dp数组,dp[i] 的含义是:前 i 天,完成所有游玩日的最少花费金额。
dp[i] 可以由前面的 dp状态 推导而来:
- 如果第 i 天不是游玩日,即第 i 天不需要考虑花钱买票,那么:dp[i] = dp[i-1]
- 如果第 i 天是游玩日,那么此时有四种选择:
- 如果第 i 天被购买的"一日票"有效期覆盖,那么:dp[i] = dp[i-1] + costs[0],其中:
dp[i – 1] 是前 i-1 天的花费
costs[0] 是第 i 天的花费,即第 i 天买了当天有效的"一日票"
- 如果第 i 天被购买的"三日票"有效期覆盖,那么:dp[i] = ( i ≥ 3 ? dp[i-3] : 0 ) + costs[1]
我们可以认为在第 i – 2 天购买了"三日票",其有效期覆盖第 i-2 天,第i-1天,第i天,这段时间的花费是 costs[1]
而第 i-2 天之前,即:第0天~第i-3天的花费是dp[i-3],此时需要注意 i – 3 可能为负数,如果为负数,则表示第 i-2 天之前没有花费,即为0
- 如果第 i 天被购买的"七日票"有效期覆盖,那么:dp[i] = ( i ≥ 7 ?dp[i-7] : 0) + costs[2]
原理同上
- 如果第 i 天被购买的"月票"有效期覆盖,那么:dp[i] = ( i ≥ 30 ?dp[i-30] : 0) + costs[3]
原理同上
因此,如果第 i 天是游玩日,那么我们可以选择上面四种花费中最小的花费。
最后只需要返回 dp[maxDay] 即可,其中maxDay为最大的游玩日,即为days[days.length – 1]。
JS算法源码
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void (async function () {
const costs = (await readline()).split(" ").map(Number);
const days = (await readline()).split(" ").map(Number);
// 最大游玩日
const maxDay = days[days.length - 1];
// dp[i] 表示 前i天中完成其中所有游玩日需要的最少花费
const dp = new Array(maxDay + 1).fill(0); // dp[0] 默认为 0, 表示前0天花费0元
// index用于指向当前需要完成的游玩日days[index]
let index = 0;
// 遍历第1天 ~ 第maxDay天
for (let i = 1; i <= maxDay; i++) {
if (i == days[index]) {
// 如果第i天是游玩日,那么此时有四种花费选择
// 选择买"一日票",该花费仅用于第i天的游玩,那么此时前i天的花费就是 dp[i-1] + cost[0]
const buy1 = dp[i - 1] + costs[0];
// 选择买"三日票",该花费可用于第i天,第i-1天,第i-2天(相当于在第i-2天购买),那么此时前i天的花费就是 dp[i-3] + cost[1]
// 需要注意,如果 i < 3,那么dp[i-3]越界,此时前(i-3)天不存在,即花费为0
const buy3 = (i >= 3 ? dp[i - 3] : 0) + costs[1];
// 选择买“七日票”,该花费可用于第i天~第i-6天(相当于在第i-6天购买),那么此时前i天的花费就是 dp[i-7] + cost[2]
// 同上,注意i<7的处理
const buy7 = (i >= 7 ? dp[i - 7] : 0) + costs[2];
// 选择买“月票”,该花费可用于第i天~第i-29天(相当于在第i-29天购买),那么此时前i天的花费就是 dp[i-30] + cost[3]
// 同上,注意i<30的处理
const buy30 = (i >= 30 ? dp[i - 30] : 0) + costs[3];
// 最终dp[i]取上面四种花费的最小值
dp[i] = Math.min(buy1, buy3, buy7, buy30);
// 匹配下一个游玩日(days是升序的,因此index 和 days[index] 正相关)
index++;
} else {
// 如果第i天不是游玩日,那么第i天不需要花费,即前i天的花费和前i-1天的花费一样
dp[i] = dp[i - 1];
}
}
console.log(dp[maxDay]);
})();
Java算法源码
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int[] costs = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
int[] days = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
// 最大游玩日
int maxDay = days[days.length - 1];
// dp[i] 表示 前i天中完成其中所有游玩日需要的最少花费
int[] dp = new int[maxDay + 1]; // dp[0] 默认为 0, 表示前0天花费0元
// index用于指向当前需要完成的游玩日days[index]
int index = 0;
// 遍历第1天~第maxDay天
for (int i = 1; i <= maxDay; i++) {
if (i == days[index]) {
// 如果第i天是游玩日,那么此时有四种花费选择
// 选择买"一日票",该花费仅用于第i天的游玩,那么此时前i天的花费就是 dp[i-1] + cost[0]
int buy1 = dp[i - 1] + costs[0];
// 选择买"三日票",该花费可用于第i天,第i-1天,第i-2天(相当于在第i-2天购买),那么此时前i天的花费就是 dp[i-3] + cost[1]
// 需要注意,如果 i < 3,那么dp[i-3]越界,此时前(i-3)天不存在,即花费为0
int buy3 = (i >= 3 ? dp[i - 3] : 0) + costs[1];
// 选择买“七日票”,该花费可用于第i天~第i-6天(相当于在第i-6天购买),那么此时前i天的花费就是 dp[i-7] + cost[2]
// 同上,注意i<7的处理
int buy7 = (i >= 7 ? dp[i - 7] : 0) + costs[2];
// 选择买“月票”,该花费可用于第i天~第i-29天(相当于在第i-29天购买),那么此时前i天的花费就是 dp[i-30] + cost[3]
// 同上,注意i<30的处理
int buy30 = (i >= 30 ? dp[i - 30] : 0) + costs[3];
// 最终dp[i]取上面四种花费的最小值
dp[i] = Math.min(Math.min(Math.min(buy1, buy3), buy7), buy30);
// 匹配下一个游玩日(days是升序的,因此index 和 days[index] 正相关)
index++;
} else {
// 如果第i天不是游玩日,那么第i天不需要花费,即前i天的花费和前i-1天的花费一样
dp[i] = dp[i - 1];
}
}
System.out.println(dp[maxDay]);
}
}
Python算法源码
# 输入获取
costs = list(map(int, input().split()))
days = list(map(int, input().split()))
# 算法入口
def getResult():
# 最大游玩日
maxDay = days[-1]
# dp[i] 表示 前i天中完成其中所有游玩日需要的最少花费
dp = [0] * (maxDay + 1) # dp[0] 默认为 0, 表示前0天花费0元
# index用于指向当前需要完成的游玩日days[index]
index = 0
# 遍历第1天~第maxDay天
for i in range(1, maxDay + 1):
if i == days[index]:
# 如果第i天是游玩日,那么此时有四种花费选择
# 选择买"一日票",该花费仅用于第i天的游玩,那么此时前i天的花费就是 dp[i-1] + cost[0]
buy1 = dp[i - 1] + costs[0]
# 选择买"三日票",该花费可用于第i天,第i-1天,第i-2天(相当于在第i-2天购买),那么此时前i天的花费就是 dp[i-3] + cost[1]
# 需要注意,如果 i < 3,那么dp[i-3]越界,此时前(i-3)天不存在,即花费为0
buy3 = (dp[i - 3] if i >= 3 else 0) + costs[1]
# 选择买“七日票”,该花费可用于第i天~第i-6天(相当于在第i-6天购买),那么此时前i天的花费就是 dp[i-7] + cost[2]
# 同上,注意i<7的处理
buy7 = (dp[i - 7] if i >= 7 else 0) + costs[2]
# 选择买“月票”,该花费可用于第i天~第i-29天(相当于在第i-29天购买),那么此时前i天的花费就是 dp[i-30] + cost[3]
# 同上,注意i<30的处理
buy30 = (dp[i - 30] if i >= 30 else 0) + costs[3]
# 最终dp[i]取上面四种花费的最小值
dp[i] = min(buy1, buy3, buy7, buy30)
# 匹配下一个游玩日(days是升序的,因此index 和 days[index] 正相关)
index += 1
else:
# 如果第i天不是游玩日,那么第i天不需要花费,即前i天的花费和前i-1天的花费一样
dp[i] = dp[i - 1]
return dp[maxDay]
# 算法调用
print(getResult())
C算法源码
#include <stdio.h>
#include <math.h>
#define MAX_SIZE 366
int main() {
int costs[4];
for (int i = 0; i < 4; i++) {
scanf("%d", &costs[i]);
}
int days[MAX_SIZE];
int days_size = 0;
while (scanf("%d", &days[days_size++])) {
if (getchar() != ' ') break;
}
// 最大游玩日
int maxDay = days[days_size - 1];
// dp[i] 表示 前i天中完成其中所有游玩日需要的最少花费
int dp[maxDay + 1];
dp[0] = 0; // dp[0] 默认为 0, 表示前0天花费0元
// index用于指向当前需要完成的游玩日days[index]
int index = 0;
// 遍历第1天~第maxDay天
for (int i = 1; i <= maxDay; i++) {
if (i == days[index]) {
// 如果第i天是游玩日,那么此时有四种花费选择
// 选择买"一日票",该花费仅用于第i天的游玩,那么此时前i天的花费就是 dp[i-1] + cost[0]
int buy1 = dp[i - 1] + costs[0];
// 选择买"三日票",该花费可用于第i天,第i-1天,第i-2天(相当于在第i-2天购买),那么此时前i天的花费就是 dp[i-3] + cost[1]
// 需要注意,如果 i < 3,那么dp[i-3]越界,此时前(i-3)天不存在,即花费为0
int buy3 = (i >= 3 ? dp[i - 3] : 0) + costs[1];
// 选择买“七日票”,该花费可用于第i天~第i-6天(相当于在第i-6天购买),那么此时前i天的花费就是 dp[i-7] + cost[2]
// 同上,注意i<7的处理
int buy7 = (i >= 7 ? dp[i - 7] : 0) + costs[2];
// 选择买“月票”,该花费可用于第i天~第i-29天(相当于在第i-29天购买),那么此时前i天的花费就是 dp[i-30] + cost[3]
// 同上,注意i<30的处理
int buy30 = (i >= 30 ? dp[i - 30] : 0) + costs[3];
// 最终dp[i]取上面四种花费的最小值
dp[i] = (int) fmin(fmin(buy1, buy3), fmin(buy7, buy30));
// 匹配下一个游玩日(days是升序的,因此index 和 days[index] 正相关)
index++;
} else {
// 如果第i天不是游玩日,那么第i天不需要花费,即前i天的花费和前i-1天的花费一样
dp[i] = dp[i - 1];
}
}
printf("%dn", dp[maxDay]);
return 0;
}
免责声明:
1、IT资源小站为非营利性网站,全站所有资料仅供网友个人学习使用,禁止商用
2、本站所有文档、视频、书籍等资料均由网友分享,本站只负责收集不承担任何技术及版权问题
3、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除下载链接并致以最深的歉意
4、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责
5、一经注册为本站会员,一律视为同意网站规定,本站管理员及版主有权禁止违规用户
6、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和IT资源小站的同意
7、IT资源小站管理员和版主有权不事先通知发贴者而删除本文
评论0