魔法甜点之和:小包的新挑战解题思路(java)| 豆包MarsCode AI刷题

67 阅读5分钟

问题描述

小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)使用哈希表**

  1. 预处理:计算并存储从0到99的所有阶乘值。
  2. 初始化:使用一个HashMap来存储状态,初始状态为"0,0"对应值为1(表示没有选择任何甜点且总和为0的情况)。
  3. 遍历每个甜点:
  • 对于当前甜点,创建一个新的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)