1799. N 次操作后的最大分数和

题目描述

给一个数组正整数nums,数组长度是2*n
规定需要对数组执行n次操作,每次操作从nums中拿出两个元素x和y,计算分数igcd(x,y)
问n次操作后的分数和最大是多少?

f1-状态压缩+动态规划

基本分析

  1. 有没有贪心的可能?没有啥思路
  2. 如果考虑用dp实现,怎么定义状态?枚举当前的选择为mask(对mask有偶数1的限制),定义f[mask]为当前选择是mask的时候的最大分数和。
  3. 基于以上dp的定义,考虑怎么转移?找到mask中1的位置,通过双层循环实现补充不漏的去掉其中2个1,mask的状态可以从以上状态中转移过来。具体地说,加入去掉的1的位置是j和k,那么f[mask] = f[i - (1<<j) - (1<<k)] + gcd[j, k] * 当前操作次数。而操作次数在i中可以体现出来(1的个数/2)
  4. 有没有预处理的需求?因为可能需要多次枚举某两个位置的最大公约数,可以定义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,存公约数需要O(m2log(n)),dpO(m22m)
空间:存公约数数组需要O(m2), 存dp结果需要O(2m)

总结

直接枚举子集和利用dp处理有啥区别?直接枚举会有很多重复计算,dp做法会利用到之前的一些结果,从这个题中看,mask可能由最多m*m个之前的状态之一转移过来。
求公约数:这里用到辗转的方法,需要记下来;另外由于转移可能会频繁使用,这里用了预处理的方式
枚举mask中2个1的位置:为了保证不重复和不漏掉,对2个位置的顺序尽心了规定,从而通过双层循环可以实现
次数系数的获取:由于是枚举mask,mask中包含了次数的信息

posted @   zhangk1988  阅读(72)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示