Wonderland
题目描述
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,默认顺序为升序。
输出描述:
完成游玩计划的最低消费 备注: 样例说明: 根据售票价格数组和游玩日期数组给出的信息,发现每次去玩的时候买一张一日票是最省钱的,所以小王会买8张一日票,每张5元,最低花费是40元。
示例1
输入
5 14 30 100
13 15 20 21 200 202 230
输出
40
Java实现
import java.util.Arrays;
import java.util.Scanner;
public class T2 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 初始化售票价格数组和游玩日期数组
int[] costs = new int[4];
int[] days;
// 输入售票价格数组
System.out.println("请输入售票价格数组(依次为一日票、三日票、周票和月票,用空格分隔):");
String[] costsInput = scanner.nextLine().split(" ");
for (int i = 0; i < 4; i++) {
costs[i] = Integer.parseInt(costsInput[i]);
}
// 输入游玩日期数组
System.out.println("请输入小王计划游玩日期数组(升序排列,用空格分隔):");
String daysInput = scanner.nextLine();
String[] daysStrArr = daysInput.split(" ");
days = Arrays.stream(daysStrArr).mapToInt(Integer::parseInt).toArray();
// 计算完成游玩计划的最低消费
int minCost = minCostTickets(costs, days);
System.out.println("完成游玩计划的最低消费为:" + minCost);
scanner.close();
}
// 计算完成游玩计划的最低消费方法
public static int minCostTickets(int[] costs, int[] days) {
int n = days.length;
int[] dp = new int[366]; // 动态规划数组
boolean[] travel = new boolean[366]; // 标记游玩日期
// 标记游玩日期
for (int day : days) {
travel[day] = true;
}
// 动态规划计算最低消费
for (int i = 1; i <= 365; i++) {
if (!travel[i]) {
dp[i] = dp[i - 1];
continue;
}
dp[i] = dp[i - 1] + costs[0]; // 单日票消费
dp[i] = Math.min(dp[i], dp[Math.max(0, i - 1)] + costs[0]); // 比较一日票
dp[i] = Math.min(dp[i], dp[Math.max(0, i - 3)] + costs[1]); // 比较三日票
dp[i] = Math.min(dp[i], dp[Math.max(0, i - 7)] + costs[2]); // 比较周票
dp[i] = Math.min(dp[i], dp[Math.max(0, i - 30)] + costs[3]); // 比较月票
}
return dp[365]; // 返回完成游玩计划的最低消费
}
}
解题思路
- 创建一个长度为365的数组dp,用于存储每一天的最低消费金额。
- 初始化dp[0]为0,表示第一天的最低消费金额为0。
- 遍历从第二天到365天的每一天:
- 如果这一天不在小王的计划游玩日期数组中,则最低消费金额与前一天相同,即dp[i] = dp[i-1]。
- 如果这一天在小王的计划游玩日期数组中,则计算四种售票方式的最低消费金额:
- 选择一日票:dp[i] = dp[i-1] + costs[0]。
- 选择三日票:dp[i] = dp[i-3] + costs[1]。注意,这里需要确保i-3大于等于0,否则不足三天无法购买三日票。
- 选择周票:dp[i] = dp[i-7] + costs[2]。同样,需要确保i-7大于等于0。
- 选择月票:dp[i] = dp[i-30] + costs[3]。需要确保i-30大于等于0。
- 返回dp[365],即完成游玩计划所需的最低消费金额。
动态规划
动态规划算法是一种通过存储中间结果来避免重复计算的优化算法,通常用于解决具有重叠子问题和最优子结构性质的问题。动态规划算法的基本思想是将原问题分解成若干个子问题,通过求解这些子问题来推导出原问题的解,同时利用已解决的子问题的结果来减少重复计算。
下面是动态规划算法的详细步骤:
确定状态:首先确定问题的状态,即描述问题局部情况的变量。状态的选择对于动态规划问题非常重要,合理的状态定义可以帮助我们理清问题的逻辑关系。
确定状态转移方程:根据问题的特点和逻辑关系,建立状态之间的转移方程,描述状态之间的转移关系。状态转移方程是动态规划问题的核心,通常通过观察问题的特点和逻辑关系来确定。
初始条件:确定初始状态的值,通常是问题中最基本或者边界的情况。
计算顺序:按照一定的计算顺序,依次计算每个状态对应的值。通常采用自底向上或者自顶向下的方式进行计算。
解决原问题:根据状态之间的关系和计算顺序,最终得到原问题的解。
动态规划算法的时间复杂度通常是子问题个数乘以解决一个子问题的时间复杂度,空间复杂度则取决于存储中间结果所需的额外空间。动态规划算法在解决一些优化问题和最优化问题时非常高效,能够显著减少重复计算,提高算法效率。