导读:一直有粉丝反馈希望算法哥多分享动态规划的题目,今天再来一道有趣的动态规划!
题目描述:
给定的整数数组 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](https://i-blog.csdnimg.cn/blog_migrate/c1032e7a63bc9780e7a1cac413c2c5f8.jpeg)
均值推导
红色方框里是根据分数基本性质得到的!由此可见,均值是固定的!恒等于:
![d6e0b9e3778f1de09781cccb70602b26.png](https://i-blog.csdnimg.cn/blog_migrate/05b787fd35eb78d9e77f6232029125df.jpeg)
均值
我们现在知道了均值是恒定的,那么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](https://i-blog.csdnimg.cn/blog_migrate/8b88837b125a7f5dc2b63527a42d464d.jpeg)
![857b0b8483c5b27f848d75f032852553.png](https://i-blog.csdnimg.cn/blog_migrate/8163af9a99a68a54572e5c39c4603b18.jpeg)
源码
动态规划在编码过程中还有一个难点,还是拿样例来说,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](https://i-blog.csdnimg.cn/blog_migrate/ba33059e85ca695ddc553b6bcda7935b.jpeg)
题目总结:
这个题目非常巧妙,有几个难点:
1:推导出均值是恒等的,排除了不少干扰信息;
2:根据算均值过程所需的和以及元素个数,想到类似0-1背包,二进制的技巧等;
3:动态规划过程中状态转移进行位运算的技巧;
4:当数据规模在10~30时,我们要有可能和二进制压缩的技巧关联,这个就靠经验了。
今天的题目你有收获吗?不要忘了点赞,转发,收藏哦!如果有没看懂的步骤直接在下面评论留言哦!