1799. N 次操作后的最大分数和
题目描述
给一个数组正整数nums,数组长度是2*n
规定需要对数组执行n次操作,每次操作从nums中拿出两个元素x和y,计算分数
问n次操作后的分数和最大是多少?
f1-状态压缩+动态规划 |
基本分析
- 有没有贪心的可能?没有啥思路
- 如果考虑用dp实现,怎么定义状态?枚举当前的选择为mask(对mask有偶数1的限制),定义f[mask]为当前选择是mask的时候的最大分数和。
- 基于以上dp的定义,考虑怎么转移?找到mask中1的位置,通过双层循环实现补充不漏的去掉其中2个1,mask的状态可以从以上状态中转移过来。具体地说,加入去掉的1的位置是j和k,那么f[mask] = f[i - (1<<j) - (1<<k)] + gcd[j, k] * 当前操作次数。而操作次数在i中可以体现出来(1的个数/2)
- 有没有预处理的需求?因为可能需要多次枚举某两个位置的最大公约数,可以定义m*m的二维数组,把结果存起来
5.dp的边界条件怎么考虑?当mask取0的时候,最大的分就是0,f[0] = 0
代码
class Solution: def gcd(self, x, y): if y == 0: return x else: return self.gcd(y, x%y) def maxScore(self, nums: List[int]) -> int: m = len(nums) # 预处理放最大公约数的值 gcd = [[0] * m for _ in range(m)] for i in range(m-1): for j in range(i+1, m): gcd[i][j] = self.gcd(nums[i], nums[j]) mask = 1<<m f = [0] * mask for i in range(mask): bits = bin(i).count('1') if bits % 2 == 1: continue for j in range(m-1): if i & (1<<j) == 0: continue for k in range(j+1, m): if i & (1<<k) == 0: continue f[i] = max(f[i], f[i - (1<<j) - (1<<k)] + int(gcd[j][k] * bits/2)) return f[-1]
复杂度
时间:假设值的范围是n,数组长度是m,存公约数需要
空间:存公约数数组需要, 存dp结果需要
总结
直接枚举子集和利用dp处理有啥区别?直接枚举会有很多重复计算,dp做法会利用到之前的一些结果,从这个题中看,mask可能由最多m*m个之前的状态之一转移过来。
求公约数:这里用到辗转的方法,需要记下来;另外由于转移可能会频繁使用,这里用了预处理的方式
枚举mask中2个1的位置:为了保证不重复和不漏掉,对2个位置的顺序尽心了规定,从而通过双层循环可以实现
次数系数的获取:由于是枚举mask,mask中包含了次数的信息
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现