c 动态数组_每日算法LeetCode805:数组的均值分割(动态规划,难度系数3/5)

导读:一直有粉丝反馈希望算法哥多分享动态规划的题目,今天再来一道有趣的动态规划!

题目描述:

给定的整数数组 A,我们要将 A数组中的每个元素移动到B数组 或者 C数组中,使得B数组的平均值和C数组的平均值相等,并且B数组和C数组都不为空。如果可以使得B数组和C数组均值相等,则返回true,否则返回false!

示例:输入: [1,2,3,4,5,6,7,8]输出: true解释: 我们可以将数组分割为 [1,4,5,8] 和 [2,3,6,7], 他们的平均值都是4.5。

注意:

  • A 数组的长度范围为 [1, 30].
  • A[i] 的数据范围为 [0, 10000].

题目分析:

第一眼看到这个题目,算法哥想到的递归枚举所有可能的B,C数组,但是我们看数据规模,A数组长度是30,每个数要么属于B,要么属于C,所有直接递归枚举有2^30次方种情况,显然是不行滴!那怎么办呢?


题目是要找到一个方案使得B,C数组的均值相等,我们从这个地方着手,假设A数组和为Sum,元素个数N,假设存在一个方案使得B,C数组均值相等,且B数组和是Sum_b,元素个数是N_b,那么C数组的和显然是Sum - Sum_b,元素个数是N - N_b且:

2c483701cbda4c3860552f18896fc0f7.png

均值推导

红色方框里是根据分数基本性质得到的!由此可见,均值是固定的!恒等于:

d6e0b9e3778f1de09781cccb70602b26.png

均值


我们现在知道了均值是恒定的,那么B数组或者C数组的的元素个数,以及元素和,怎么处理呢 ?考察数组B,要从A数组选择一些数存入B数组,使得B数组的和除以B数组元素个数等于我们前面的均值,算法哥马上想到类似0-1背包问题,用dp[i][j]表示在数组A里,前i个数里选择若干个数加入到数组B里,得到的和为j,那在这个状态下我们怎么记录B数组和为j时,B数组里的元素个数呢?直接存在dp[i][j]里 ?但是可能和为j时,有多种方案,比如题目样例,dp[2][3]表示前3个数(下标从0开始的)选取若干个数和为3,此时1+2 和3就有两个方案满足,直接存在dp[2][3]的value里存不下!


注意到数据的规模,A数组元素个数最大是30!哈哈,我们是不是可以利用dp[i][j]=X的二进制表示来想点办法!因为dp[i][j]是int型,有32个bit位,比特位的第k位正好可以表示前i个数里选择k个数字,和为j!那么前面的dp[2][[3]=3, 为什么等于3?3的二进制表示(00000000 00000000 00000000 00000011)倒数第位和第位表示前3个数里选择1个(3)或者2个(1+2)数,可以使得和为3!也就是说我们可以利用dp[i][j]的二进制位,把和为j的元素个数在bit位上存下来!真是太巧妙了!


现在我们可以模拟0-1背包的过程,把所有可能的dp[i][j]算出来,然后每个和为j的元素个数也保存在dp[i][j]的二进制bit位里,算均值就 so easy啦!直接用j除以二进制位里为1的序号就等于均值!

上源码:

16e975955bdaf483172ebe5c215a8bf6.png
857b0b8483c5b27f848d75f032852553.png

源码

动态规划在编码过程中还有一个难点,还是拿样例来说,A数组[1,2,3,4,5,6,7,8],我们现在知道dp[2][3]=3(低字节00000011),那dp[3][7]等于什么呢 ?第三个元素等于4,也就是说dp[3][7]可以由dp[2][3]推导过来,因为我们选择4进来即可!那dp[3][7]的二进制位应该等于什么呢 ?因为新进来一个4,是不是意味着原先dp[2][3]的方案里的元素个数都加1了,那不就是直接把dp[2][3]左移一位吗!真是太巧妙了!哈哈!同理,代码里第28行,还有一个或运算,就留给读者自己思考吧,如果不清楚可以留言,算法哥再解释!


复杂度分析:

dp[i][j]最大是dp[30][10000 * 30],所以时间复杂度是O(n * 10000 * n)=O(n^2 * 10000)的!

a5cb02ca14fd0861243228bfebc4be64.png

题目总结:

这个题目非常巧妙,有几个难点:

1:推导出均值是恒等的,排除了不少干扰信息;

2:根据算均值过程所需的和以及元素个数,想到类似0-1背包,二进制的技巧等;

3:动态规划过程中状态转移进行位运算的技巧;

4:当数据规模在10~30时,我们要有可能和二进制压缩的技巧关联,这个就靠经验了。

今天的题目你有收获吗?不要忘了点赞,转发,收藏哦!如果有没看懂的步骤直接在下面评论留言哦!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值