首页 > 其他分享 >leetcode每日一题:3181.执行操作可获得的最大总奖励 II

leetcode每日一题:3181.执行操作可获得的最大总奖励 II

时间:2024-10-26 16:48:19浏览次数:3  
标签:1111 00 last II rewardValues 3181 0000 now leetcode

 题干:

读本文前,请先弄懂上一篇中的内容,因为这是对上一篇内容的优化:

3180.执行操作可获得的最大总奖励I

明白上篇的,访问值的影响、复制、上下行之间的关系和算法后可继续看:

上一篇中,我们用二维数组,第二维表示了状态空间。

但是,在今日的题目中,提交不行,因为占用的空间太太太大了

关键:我们可以把每行用一个二进制数表示


转化为二进制后:
第一行:1000 0000 0000 0000 00
第二行:1100 0000 0000 0000 00
第三行:1101 1000 0000 0000 00
第四行:1101 1101 0000 0000 00
第五行:1101 1111 0111 0000 00
第六行:1101 1111 0111 1111 10
这样我们节省了太多的空间了
不过像这样 0 -> 17 左小右大实际不好操作,因为上述的二进制值右边才是小的数

左右调转一个方向,这样,小的数和小的位置对应上了
即:
00 0000 0000 0000 0001
00 0000 0000 0000 0011
00 0000 0000 0001 1011
00 0000 0000 1011 1011
00 0000 1110  1111  1011
01 1111  1110  1111  1011
接下来要做的就是把行复制/更新那些操作,在二进制数上实现
原来代码:
class Solution(object):
    def maxTotalReward(self, rewardValues):
        #先从小到大排序,易于计算
        rewardValues.sort()
        n = len(rewardValues) + 1
        m = rewardValues[n-2] * 2 + 1 
        #f用于记录每次访问元素后,状态空间各值可到达情况,初始全为False
        f =  [[False for _ in range(m)] for _ in range(n)]
        
        #第一行单独处理
        f[0][0] = True
        #从第二行开始
        for i in range(1,n):
            #第i行时对应第i-1元素
            v = rewardValues[i-1]
            for j in range(0,m):
                #复制下来
                f[i][j] = f[i-1][j]
                #v~v*2-1特殊处理
                if j>=v and j<2*v:
                    f[i][j] = f[i][j] or f[i-1][j-v]#取/不取 有一个可以达到就行
        
        for j in range(m-1,-1,-1):
            if f[n-1][j] :
                return j
原来用于记录的二维数组:f [ n ] [ m ]    ---->   f [ n ]
原来for循环中复制上一行:f [ i ][ j ] = f [ i -1 ][ j ]  ---->  f [ i+1 ] = f [ i ] 直接整行复制
v~v *2 -1特殊处理:这是重点和难点
拿表中5、6行举例吧 ,6行时访问的是9
5行:00 0000 1110  1111  1011
6行:01 1111  1110  1111  1011
怎么由这个第5行和数字9得到第6行呢?
先完整复制下来得到: 00 0000 111 0  1111  1011   (蓝色这部分是不会被影响的) (红色是特殊处理的v ~ v * 2 -1 )
红1的结果 = 红1 | 蓝1 = 1 | 1= 1   (红蓝1指的是红蓝色部分从右往左数第1位)
红2的结果 = 红2 | 蓝2 = 1 | 1= 1
...... 
最终有:第6行= 01 1111 1110 1111 1011

如图所示:


重点是蓝色那里
比如第5行 = line5  当前访问值为9
则蓝色部分的操作就是:先保留x的0~8位,再把这9位数向左平移9位
即:
blue = 保留line5的0~8位 ,将0~8位与1求与就保留下来了即: blue = line5 & ( 1 << 9 -1 ) 
blue = blue << 9

然后 line 6 = line5 | blue
抽象出来就是:
上一行-line_last 、当前行-line_now 、当前访问值 v
操作为:
line_now = line_last
blue = line_last & ( 1 << v -1 )
line_now = line_now | (blue  <<  v)

分析完了上代码: 

func maxTotalReward(rewardValues []int) int {
    sort.Ints(rewardValues)
    n := len(rewardValues)
    //特殊处理:如果有maxV与maxV-1两个值,结果直接出
    if n >= 2 && (rewardValues[n-2] == rewardValues[n-1]-1) {
        return rewardValues[n-1] * 2 - 1
    }

    //f用于记录每次访问后的结果
    f := make([]int,n+1)
    //标记0值可以达到
    f[0] = 1
    //遍历rewardValues, i是下标0~n-1
    for i,v := range rewardValues {
        //先把上一行复制下来
        f[i+1] = f[i]
        //特殊处理 v ~ v*2 -1 
            //上次访问的 0 ~ v-1部分 会用于 本次访问的v ~ v*2-1部分
            //用位求与  取出上次访问的 0 ~ v-1部分  比如访问3时要保留0~2,则要与000...00111求与保留0~2 (访问x保留x位)
            last_0_to_vsub1 := f[i] & ((1 << v) - 1)
            f[i+1] = f[i+1] | (last_0_to_vsub1 << v)
    }

    //找到二进制数的长度
    res := bits.Len(uint(f[n]))
    //由于第一位代表0不是1所以减1
    res -= 1
    return res
}

我们可以看到,虽然我们创建了长度为n+1的数组f

但是我们其实每次只会用上一次的结果,再早的丢掉也没关系

如此可以优化空间复杂度,优化代码如下:

func maxTotalReward(rewardValues []int) int {
    sort.Ints(rewardValues)
    n := len(rewardValues)
    //特殊处理:如果有maxV与maxV-1两个值,结果直接出
    if n >= 2 && (rewardValues[n-2] == rewardValues[n-1]-1) {
        return rewardValues[n-1] * 2 - 1
    }

    //f_last是上次结果(上一行),f_now是当前行
    f_last,f_now := 1,0
    //遍历rewardValues, i是下标0~n-1
    for _,v := range rewardValues {
        //先把上一行复制下来
        f_now = f_last
        //特殊处理 v ~ v*2 -1 
            //上次访问的 0 ~ v-1部分 会用于 本次访问的v ~ v*2-1部分
            //用位求与  取出上次访问的 0 ~ v-1部分  比如访问3时要保留0~2,则要与000...00111求与保留0~2 (访问x保留x位)
            last_0_to_vsub1 := f_last & ((1 << v) - 1)
            f_now = f_now | (last_0_to_vsub1 << v)
        //更新f_last
        f_last = f_now
    }

    //找到二进制数的长度
    res := bits.Len(uint(f_now))
    //由于第一位代表0不是1所以减1
    res -= 1
    return res
}

由于int型的数,可以表示二进制的位数,非常非常有限,稍微大一点的数几百,就会导致溢出。
所以提交运行会报错。
所以考虑使用可以存放更多位的数的数据类型:

参考官方题解,使用big.Int 它能够处理任意大小的整数,适合高精度计算或大数运算的需求
big.Int的左移:X.Lsh(被移数,移动量)
big.Int的位与:X.And(值1,值2)

big.Int的位或:X.Or(值1,值2)
修改后的代码如下:
逻辑和计算完全没变,就是看起来稍微复杂了点,注释解释了对应了上面的哪一步

func maxTotalReward(rewardValues []int) int {
    sort.Ints(rewardValues)
    n := len(rewardValues)
    //特殊处理:如果有maxV与maxV-1两个值,结果直接出
    if n >= 2 && (rewardValues[n-2] == rewardValues[n-1]-1) {
        return rewardValues[n-1] * 2 - 1
    }

    //f_last,f_now分别代表上一行,当前行
    f_last,f_now := big.NewInt(1),big.NewInt(0)
    //遍历rewardValues
    for _,v := range rewardValues {
        //先把上一行复制下来
        f_now = f_last
        //特殊处理 v ~ v*2 -1   last_0_to_vsub1 := f_last & ((1 << v) - 1)
            //1 << v -1
            reserved := big.NewInt(1)     // 1 
            reserved.Lsh(reserved,uint(v))// 1 << v
            reserved.Sub(reserved, big.NewInt(1)) // 1 << v -1
            //f_last & ((1 << v) - 1)
            last_0_to_vsub1 := new(big.Int).And(f_last,reserved)
            //f[i+1] = f[i+1] | (last_0_to_vsub1 << v)
            f_now.Or( f_now, last_0_to_vsub1.Lsh(last_0_to_vsub1, uint(v)) )
        f_last = f_now
    }

    //找到二进制数的长度,并-1
    res := f_now.BitLen() - 1
    //由于第一位代表0不是1所以减1
    return res
}

其实到这里,已经和力扣官方的题解一样了。只是我写的开了一些。

标签:1111,00,last,II,rewardValues,3181,0000,now,leetcode
From: https://blog.csdn.net/IRON_MANNN/article/details/143251701

相关文章

  • 力扣每日一题3181.执行操作可获得的最大总奖励2
      题目描述:给你一个整数数组 rewardValues,长度为 n,代表奖励的值。最初,你的总奖励 x 为0,所有下标都是 未标记 的。你可以执行以下操作 任意次 :从区间 [0,n-1] 中选择一个 未标记 的下标 i。如果 rewardValues[i] 大于 你当前的总奖励 x,则将 rewardVa......
  • LeetCode 3181. 执行操作可获得的最大总奖励 II
    1classSolution{2public:3intmaxTotalReward(vector<int>&rewardValues){4intm=ranges::max(rewardValues);5unordered_set<int>s;6for(intv:rewardValues){7if(s.contains(v))......
  • leetcode-1934-确认率
    链接:1934.确认率-力扣(LeetCode)前提条件:Signups+----------------+----------+|ColumnName|Type|+----------------+----------+|user_id|int||time_stamp|datetime|+----------------+----------+User_id是该表的主键。每一行......
  • 代码随想录算法训练营第七天|LeetCode 344.反转字符串、LeetCode 541.反转字符串Ⅱ、
    LeetCode 344.反转字符串题目链接:LeetCode344.反转字符串文章链接:代码随想录题目:编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用O(1)的额外空间解决这一问题。示......
  • LeetCode|3180. 执行操作可获得的最大总奖励 I(day23)
    作者:MJ昊博客:掘金、CSDN等公众号:程序猿的编程之路今天是昊的算法之路第23天,今天分享的是LeetCode第3180题执行操作可获得的最大总奖励I的解题思路。这是一道中等难度的题目,要求我们在给定的奖励值数组中,通过某些操作尽可能获取最大总奖励。题目描述简要回顾题目要......
  • LeetCode|384. 打乱数组(day22)
    作者:MJ昊博客:掘金、CSDN等公众号:程序猿的编程之路今天是昊的算法之路第22天,今天分享的是LeetCode第384题打乱数组的解题思路。这是一道中等难度的题目,要求我们实现一个算法,使得数组支持随机打乱和重置为初始顺序的功能,并且每种排列出现的概率应当相等。题目描述简要......
  • LeetCode|910. 最小差值 II(day19)
    作者:MJ昊博客:掘金、CSDN等公众号:程序猿的编程之路今天是昊的算法之路第19天,今天分享的是LeetCode第910题最小差值II的解题思路。这是一道中等难度的题目,考察如何通过调整数组中的数值来最小化最大值与最小值之间的差距。题目描述简要回顾给定一个整数数组nums和......
  • IIS使用反向代理,解决路径包含特殊字符无法访问的问题
    环境:操作系统:WindowsServer2019IIS版本:10问题试用Nocobase的时候,遇到400BadRequest的报错。 直接访问的话,报错页面是非常常见的RuntimeError。诡异这个问题,在开发端(Windows11)的IIS中不会出现。同样的IIS版本。解决办法先说结论,时间比较赶的朋友直接试这个就可以......
  • 2024-10-23-leetcode每日一题-构成整天的下标对数目 II
    题目描述给你一个整数数组 hours,表示以 小时 为单位的时间,返回一个整数,表示满足 i<j 且 hours[i]+hours[j] 构成 整天 的下标对 i, j 的数目。整天 定义为时间持续时间是24小时的 整数倍 。例如,1天是24小时,2天是48小时,3天是72小时,以此类推。......
  • iis部署tms web core
    iis部署tmswebcore首先准备好你要发布的网站文件夹 1)iis设置网站2)1、打开“IIS信息服务管理器”——》选择你发布的网站——》选择功能视图中的“身份验证”——》右键匿名身份验证,选择“编辑”,选择“特定用户IUSR”;2、右键要发布的网站文件夹,选择“安全”——》“编辑......