问题描述
小R不再追求甜点中最高的喜爱值,今天他想要的是甜点喜爱值之和正好匹配他的预期值 S。为了达到这个目标,他可以使用魔法棒来改变甜点的喜爱值,使其变为原来喜爱值的阶乘。每个甜点只能使用一次魔法棒,也可以完全不用。
下午茶小哥今天带来了 N 个甜点,每个甜点都有一个固定的喜爱值。小R有 M 个魔法棒,他可以选择任意甜点使用,但每个甜点只能使用一次魔法棒。他的目标是通过选择一些甜点,可能使用魔法棒,使得这些甜点的喜爱值之和恰好为 S。
请计算小R有多少种不同的方案满足他的要求。如果两种方案中,选择的甜点不同,或者使用魔法棒的甜点不同,则视为不同的方案。
测试样例
样例1:
输入:n = 3, m = 2, s = 6, like = [1, 2, 3] 输出:5
样例2:
输入:n = 3, m = 1, s = 1, like = [1, 1, 1] 输出:6
样例3:
输入:n = 5, m = 3, s = 24, like = [1, 2, 3, 4, 5] 输出:1
样例4:
输入:n = 4, m = 0, s = 10, like = [1, 3, 3, 3] 输出:1
样例5:
输入:n = 6, m = 1, s = 35, like = [5, 5, 5, 5, 5, 5] 输出:0
方法思路
动态规划 (DP)使用哈希表**
- 预处理:计算并存储从0到99的所有阶乘值。
- 初始化:使用一个HashMap来存储状态,初始状态为"0,0"对应值为1(表示没有选择任何甜点且总和为0的情况)。
- 遍历每个甜点:
- 对于当前甜点,创建一个新的HashMap来保存当前状态。
- 遍历当前状态中的每一个键值对:
- 提取当前状态的魔法棒使用次数a和喜爱值之和b。
- 如果不使用魔法棒且总和不超过目标值S,则更新新的状态。
- 如果使用魔法棒且总和不超过目标值S,则更新新的状态。
- 结果计算:遍历所有可能的状态,累加总和等于S的情况。
具体流程举例:
n = 3, m = 2, s= 6, like = [1, 2, 3].
预处理:计算并存储从0到99的所有阶乘值。
初始化:f初始化为{"0,0" -> 1}。
遍历每个甜点:
- 第一个甜点(喜爱值为1):
- 当前状态:{"0,0" -> 1}
- 不使用魔法棒:{"0,1" -> 1}
- 使用魔法棒:{"1,1" -> 1}
- 更新后的状态:{"0,0" -> 1, "0,1" -> 1, "1,1" -> 1}
- 第二个甜点(喜爱值为2):
- 当前状态:{"0,0" -> 1, "0,1" -> 1, "1,1" -> 1}
- 不使用魔法棒:
- 从"0,0"得到"0,2"
- 从"0,1"得到"0,3"
- 从"1,1"得到"1,3"
- 使用魔法棒:
- 从"0,0"得到"1,2"
- 从"0,1"得到"1,3"
- 从"1,1"得到"2,3"
- 更新后的状态:{"0,0" -> 1, "0,1" -> 1, "1,1" -> 1, "0,2" -> 1, "0,3" -> 1, "1,3" -> 2, "1,2" -> 1, "2,3" -> 1}
- 第三个甜点(喜爱值为3):
- 当前状态:{"0,0" -> 1, "0,1" -> 1, "1,1" -> 1, "0,2" -> 1, "0,3" -> 1, "1,3" -> 2, "1,2" -> 1, "2,3" -> 1}
- 不使用魔法棒:
- 从"0,0"得到"0,3"
- 从"0,1"得到"0,4"
- 从"1,1"得到"1,4"
- 从"0,2"得到"0,5"
- 从"0,3"得到"0,6"
- 从"1,3"得到"1,6"
- 从"1,2"得到"1,5"
- 从"2,3"得到"2,6"
- 使用魔法棒:
- 从"0,0"得到"1,6"
- 从"0,1"得到"1,7"
- 从"1,1"得到"2,7"
- 从"0,2"得到"1,8"
- 从"0,3"得到"1,9"
- 从"1,3"得到"2,9"
- 从"1,2"得到"2,8"
- 从"2,3"得到"3,9"
- 更新后的状态:{"0,0" -> 1, "0,1" -> 1, "1,1" -> 1, "0,2" -> 1, "0,3" -> 2, "1,3" -> 2, "1,2" -> 1, "2, 3" -> 1, "0,4" -> 1, "1,4" -> 1, "0,5" -> 1, "0,6" -> 2, "1,6" -> 2, "1,5" -> 1, "2,6" -> 1, "1,7" -> 1, "2,7" -> 1, "1,8" -> 1, "1,9" -> 1, "2,9" -> 1, "3,9" -> 1}
结果计算:总和为6的情况有:"0,6" -> 2, "1,6" -> 2, "2,6" -> 1,结果为:2 + 2 + 1 = 5。
代码实现
动态规划 (DP)使用哈希表
import java.util.HashMap;
import java.util.Map;
public class Main {
private static long[] magic = new long[100]; // 阶乘数组
// 计算阶乘并存储在magic数组中
private static void pre() {
magic[0] = 1; // 0! 定义为 1
for (int i = 1; i < 100; i++) {
magic[i] = magic[i - 1] * i;
}
}
public static int solution(int n, int m, int s, int[] like) {
pre(); // 初始化阶乘数组
Map<String, Long> f = new HashMap<>(); // 使用HashMap来代替defaultdict
f.put("0,0", 1L); // 初始状态
for (int i = 1; i <= n; i++) {
Map<String, Long> g = new HashMap<>(f); // 复制当前状态
for (Map.Entry<String, Long> entry : g.entrySet()) {
String key = entry.getKey();
long v = entry.getValue();
String[] parts = key.split(",");
int a = Integer.parseInt(parts[0]);
int b = Integer.parseInt(parts[1]);
if (b + like[i - 1] <= s) { // 加入"喜欢"的数量
String newKey = a + "," + (b + like[i - 1]);
f.put(newKey, f.getOrDefault(newKey, 0L) + v);
}
if (a + 1 <= m && b + magic[like[i - 1]] <= s) { // 加入"喜欢的阶乘"
String newKey = (a + 1) + "," + (b + (int)magic[like[i - 1]]);
f.put(newKey, f.getOrDefault(newKey, 0L) + v);
}
}
}
long sum = 0;
for (int i = 0; i <= m; i++) {
sum += f.getOrDefault(i + "," + s, 0L); // 累加满足条件的结果
}
return (int)sum;
}
public static void main(String[] args) {
int[] like1 = {1, 2, 3};
int[] like2 = {1, 1, 1};
System.out.println(solution(3, 2, 6, like1) == 5);
System.out.println(solution(3, 1, 1, like2) == 6);
}
}
时空复杂度
- 时间复杂度:时间复杂度都大约为O(nms),其中n是甜点的数量,m是可用魔法棒的数量,s是目标总和。
- 空间复杂度:基于哈希表的的空间复杂度取决于实际产生的状态数量,同时间复杂度一样都大约为O(nms)