Maximum Product of Subsequences With an Alternating Sum Equal to K

Maximum Product of Subsequences With an Alternating Sum Equal to K

You are given an integer array nums and two integers, k and limit. Your task is to find a non-empty subsequence of nums that:

  • Has an alternating sum equal to k.
  • Maximizes the product of all its numbers without the product exceeding limit.

Return the product of the numbers in such a subsequence. If no subsequence satisfies the requirements, return -1.

The alternating sum of a 0-indexed array is defined as the sum of the elements at even indices minus the sum of the elements at odd indices.

 

Example 1:

Input: nums = [1,2,3], k = 2, limit = 10

Output: 6

Explanation:

The subsequences with an alternating sum of 2 are:

  • [1, 2, 3]
    • Alternating Sum: 1 - 2 + 3 = 2
    • Product: 1 * 2 * 3 = 6
  • [2]
    • Alternating Sum: 2
    • Product: 2

The maximum product within the limit is 6.

Example 2:

Input: nums = [0,2,3], k = -5, limit = 12

Output: -1

Explanation:

A subsequence with an alternating sum of exactly -5 does not exist.

Example 3:

Input: nums = [2,2,3,3], k = 0, limit = 9

Output: 9

Explanation:

The subsequences with an alternating sum of 0 are:

  • [2, 2]
    • Alternating Sum: 2 - 2 = 0
    • Product: 2 * 2 = 4
  • [3, 3]
    • Alternating Sum: 3 - 3 = 0
    • Product: 3 * 3 = 9
  • [2, 2, 3, 3]
    • Alternating Sum: 2 - 2 + 3 - 3 = 0
    • Product: 2 * 2 * 3 * 3 = 36

The subsequence [2, 2, 3, 3] has the greatest product with an alternating sum equal to k, but 36 > 9. The next greatest product is 9, which is within the limit.

 

Constraints:

  • 1 <= nums.length <= 150
  • 0 <= nums[i] <= 12
  • -105 <= k <= 105
  • 1 <= limit <= 5000

 

解题思路

  dp 还是很容易想到的,就是不好处理乘积不超过 $\text{limit}$ 这个条件。显然我们不能直接像 01 背包那样 dp 求个满足交错和等于 $k$ 的子序列的乘积最大值,因为有可能会超过 $\text{limit}$,为此我们需要把 $\text{limit}$ 当作 dp 状态的一维。

  先不管会不会超时或爆空间,定义布尔状态 $f(i,j,k,u \in \{1/-1\},v \in \{0/1\})$ 表示是否存在由前 $i$ 个元素构成交错和为 $j$,且乘积为 $k$,下一个元素在交错和中的系数是 $1/-1$,序列是否为空 $(0/1)$ 的子序列。为了方便,这里考虑当前状态可以转移到哪些状态。根据选或不选第 $i+1$ 个元素进行状态转移:

\begin{cases}
f(i,j,k,u,v) \to f(i+1,j + u \cdot a_{i+1},k \cdot a_{i+1}, -u, 1) \\
f(i,j,k,u,v) \to f(i+1,j,k,u,v)
\end{cases}

  考虑 $n$,$k$,$\text{limit}$ 的最大值,状态的数量级大约为 $10^{11}$。不过容易发现,由于 $n$ 最大只有 $150$,交错和不可能达到 $10^5$ 的级别。事实上考虑最糟糕的情况,即 $n = 150$,偶数位都是 $12$,奇数位都是 $0$,此时有最大交错和 $\frac{150}{2} \cdot 12 = 900$。所以实际上状态的数量级应该约为 $10^9$,不过还是很大就是了。

  实际上我们应该更关注乘法有多少种可能的结果,$150$ 个 $0 \sim 12$ 的数相乘,能得到多少种不同的结果。通过打表可以发现,实际上只有 $395$ 种不同结果,远远小于 $5000$!是怎么注意到种类很少的呢?这是因为相乘的结果只能通过 $12$ 以内的质数($2,3,5,7,11$)组合得到,因此直觉上会猜相乘结果的种类很少。用到这个技巧的还有 Count Beautiful Numbers

打表代码
#include <bits/stdc++.h>
using namespace std;

int main() {
    unordered_set<int> st({1});
    for (int i = 1; i <= 150; i++) {
        unordered_set<int> t;
        for (int j = 0; j <= 12; j++) {
            for (auto &x : st) {
                int p = x * j;
                if (p <= 5000) t.insert(p);
            }
        }
        cout << i << ": " << t.size() << '\n';
        st.insert(t.begin(), t.end());
    }
    
    return 0;
}

  另外注意到,对于一个相乘结果不超过 $5000$ 的子序列,其中大于 $1$ 的值的数量不超过 $\left\lfloor \log{5000} \right\rfloor = 12$ 个。对应到子序列的交错和中,考虑最粗略的情况,即有 $12$ 个 $12$ 在偶数位,其余都是 $1$,此时的交错和也只有 $(12-1) \cdot 12 + (1-1) (75-12) = 132$,因此交错和实际上也是很少的。

  所以实际上状态的数量级最大只有 $10^6$。

  如果用循环递推进行 dp 则需要对可能的交错和以及乘积结果进行离散化,很麻烦。所以用记忆化搜索,开个 std::set<std::array<int, 5>> 来记录搜索过的状态避免重复搜索就行。另外需要注意处理乘积结果超过 $\text{limit}$ 的情况,剩下的细节见代码。

  AC 代码如下:

class Solution {
public:
    int maxProduct(vector<int>& nums, int k, int limit) {
        int n = nums.size();
        if (abs(k) > (n + 1) / 2 * 12) return -1;
        set<array<int, 5>> st;
        int ret = -1;
        auto dfs = [&](auto &&dfs, int u, int s, int p, int c, int f) {
            if (u == n) {
                if (s == k && p <= limit && f) ret = max(ret, p);
                return;
            }
            if (st.count({u, s, p, c, f})) return;
            st.insert({u, s, p, c, f});
            dfs(dfs, u + 1, s, p, c, f);
            dfs(dfs, u + 1, s + c * nums[u], min(limit + 1, p * nums[u]), -c, 1);
        };
        dfs(dfs, 0, 0, 1, 1, 0);
        return ret;
    }
};

 

参考资料

  暴力出奇迹:如何分析状态个数(Python/Java/C++/Go):https://leetcode.cn/problems/minimum-operations-to-make-elements-within-k-subarrays-equal/solution/hua-dong-chuang-kou-zhong-wei-shu-hua-fe-e9cn/

posted @ 2025-04-08 09:53  onlyblues  阅读(64)  评论(0)    收藏  举报
Web Analytics