周赛2172. 数组的最大与和

题目

给你一个长度为 n 的整数数组 nums 和一个整数 numSlots ,满足2 * numSlots >= n 。总共有 numSlots 个篮子,编号为 1 到 numSlots 。

你需要把所有 n 个整数分到这些篮子中,且每个篮子 至多 有 2 个整数。一种分配方案的 与和 定义为每个数与它所在篮子编号的 按位与运算 结果之和。

比方说,将数字 [1, 3] 放入篮子 1 中,[4, 6] 放入篮子 2 中,这个方案的与和为 (1 AND 1) + (3 AND 1) + (4 AND 2) + (6 AND 2) = 1 + 1 + 0 + 2 = 4 。

请你返回将 nums 中所有数放入 numSlots 个篮子中的最大与和。
提示:

n == nums.length
1 <= numSlots <= 9
1 <= n <= 2 * numSlots
1 <= nums[i] <= 15

示例:

输入:nums = [1,2,3,4,5,6], numSlots = 3
输出:9
解释:一个可行的方案是 [1, 4] 放入篮子 1 中,[2, 6] 放入篮子 2 中,[3, 5] 放入篮子 3 中。
最大与和为 (1 AND 1) + (4 AND 1) + (2 AND 2) + (6 AND 2) + (3 AND 3) + (5 AND 3) = 1 + 0 + 2 + 2 + 3 + 1 = 9 。

思路

题目中说明有numSlots个篮子,每个篮子最多装两个,最多有9个篮子。且提示里含有2 * numSlots,因此我们转化一下,相当于有2 * numSlots个篮子,每个篮子最多装一个数字。
数组长度最多为15,可以考虑往枚举和状态压缩方向考虑。虽然数组内数字可以保持相同,但是每个篮子是不一样的,因此可以对篮子进行状态压缩:
采用二进制的方式,每一位代表一个篮子,每一位的值1代表篮子里有数字,0代表篮子里没有数字。
这样可以枚举所有的2^2 * numSlots^ 种可能性,最后找到最大的结果。

但是此时还有一个问题:我们虽然通过状态压缩能枚举出每个篮子是否有数字,但是我们并不能明确知道篮子中装的是哪个数字。
此时还需要枚举出每个篮子中可能装有的数字,并找出其中与和的最大值。
我们可以从没有装有数字的篮子组合开始:枚举所有数字,找出其中和真实篮子(假设虚拟2 * numSlots个篮子,在第j个篮子时,其真实篮子应该是j/2+1)与运算后最大的值。
随后是有1个篮子有数字,有2个篮子有数字…有len-1个篮子有数字,均找出其中最大值。
直到所有数字都装满。

此时可以发现,这种思路有点像动态规划,因此我们可以写出状态转移方程:
在这里插入图片描述
式中:i为枚举的2^2 * numSlots^ 种篮子组合可能性的一种;i|(1<<j)为第i个篮子组合的基础上,增加一个含有数字的篮子,此时i可能性中篮子中含有数字的数量必须比数组长度小(i的位中为1的数量比所有数字量少);sum1是i的所有位中1的数量,实质为含有数字的篮子的个数;(j/2+1)&nums[sum1]是指第j个篮子放入数字nums[sum1]:
为何这里是放入第sum1个数字,这是因为枚举篮子组合时是从没有(sum1=0)篮子有数字开始,到nums.size()(sum1=nums.size())个篮子都有数字结束。而对每个篮子组合加入的新篮子都需要放一个数字,干脆就放nums[sum1],这样也不需要重新定义一个变量去标记这个问题。

这个方程的意思是,在i的二进制j位上(即第j个篮子,且该篮子原来并没有放数字)放入一个数字,观察是不是比原来f[i|(1<<j)]大,借此寻找最大值

代码

class Solution {
public:
    int maximumANDSum(vector<int>& nums, int numSlots) {
        int maxx = 0;
        vector<int> f(1<<(2*numSlots));   //用来记录dp过程中存储的值
        for(int i=0;i<f.size();i++){
            int n = i;
            int sum1 = 0;
            //记录i的二进制中1的个数
            while(n){
                if((n&1)==1)
                    sum1++;
                n>>=1;
            }
            //如果1的个数大于nums.size(),不再在篮子里加数字
            if(sum1>=nums.size()) continue;
            for(int j=0;j<2*numSlots;j++){
            	//如果i的j位上值为0
                if((i&(1<<j))==0){
                    int s = i|(1<<j);
                    //更新f[s]的值
                    f[s] = max(f[s], f[i]+((j/2+1)&nums[sum1]));
                    //更新最大值
                    maxx = max(f[s], maxx);
                }
            }
        }
        return maxx;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值