N 次操作后的最大分数和

200 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情

N 次操作后的最大分数和

给你 nums ,它是一个大小为 2 * n 的正整数数组。你必须对这个数组执行 n 次操作。

在第 i 次操作时(操作编号从 1 开始),你需要:

  • 选择两个元素 x 和 y 。
  • 获得分数 i * gcd(x, y) 。
  • 将 x 和 y 从 nums 中删除。

请你返回 n 次操作后你能获得的分数和最大为多少。

函数 gcd(x, y) 是 x 和 y 的最大公约数。

 

示例 1:

输入: nums = [1,2]
输出: 1
解释: 最优操作是:
(1 * gcd(1, 2)) = 1

示例 2:

输入: nums = [3,4,6,8]
输出: 11
解释: 最优操作是:
(1 * gcd(3, 6)) + (2 * gcd(4, 8)) = 3 + 8 = 11

示例 3:

输入: nums = [1,2,3,4,5,6]
输出: 14
解释: 最优操作是:
(1 * gcd(1, 5)) + (2 * gcd(2, 4)) + (3 * gcd(3, 6)) = 1 + 4 + 9 = 14

 

提示:

  • 1 <= n <= 7
  • nums.length == 2 * n
  • 1 <= nums[i] <= 10^6

思路

看一眼范围,发现最多只有14个数,有多少种方案呢,暴力枚举可行不呢?

一共最多有C142×C122×C102×C82×C62×C42×C22C_{14}^2 \times C_{12}^2 \times C_{10}^2 \times C_{8}^2 \times C_{6}^2 \times C_{4}^2 \times C_{2}^2 这么多种方案

化简一下方案数应该是 14!27=681080400\frac{14!}{2^7} = 681080400 种方案

看得出来,在1s之内跑完可能不行。

如何优化呢?

换个角度思考。每一层的状态会不会发生重叠,在这个过程中,是不是很多种方案都重复计算了一些答案。

一共有多少种状态?

最多14个数,一共有 2142^{14} 种状态。

但是,由于每一层上,选择两个数的代价并不一样。一共有7层,所以,总共完全的状态数量应该是

214×7=1146882^{14} \times 7 = 114688 种状态。

可以看出,总状态数是很少的,并且有些状态根本不会转移到。

于是,方法就明确了,采用记忆化搜索的方式,若是当前dep层的这个状态搜索过(下面的值计算过最大值的情况),直接返回即可。

采用了这个优化之后,本题就变成了一个枚举最多7层的dfs遍历方案数问题。

注意:gcd的运算时间尽管很少,但是可以先算出来,而不是每一次都算一次。

代码

class Solution {
public:
    int maxScore(vector<int>& nums) {
        int n = nums.size();
        vector<vector<int>> back(n/2+1, vector<int>(1<<n));
        vector<vector<int>> gcd(n, vector<int>(n));
        for (int i = 0; i < n; i ++)
        for (int j = i+1; j < n; j ++)
            gcd[i][j] = __gcd(nums[i], nums[j]);
        function<int(int, int)> dfs
        =[&](int dep, int state) -> int {
            if (state == 0) return 0;
            if (back[dep][state]) return back[dep][state];
            int now = 0;
            for (int i = 0; i < n; i ++)
                if (state >> i & 1)
            for (int j = i+1; j < n; j ++)
                if (state >> j & 1) {
                    int mask = state ^ (1 << i) ^ (1 << j);
                    now = max(now, 
                    dep * gcd[i][j] + dfs(dep + 1, mask));
                }
            back[dep][state] = now;
            return now;
        };
        return dfs(1, (1<<n) - 1);
    }
};