【位运算 试填法】【推荐】3022. 给定操作次数内使剩余元素的或值最小

简介: 【位运算 试填法】【推荐】3022. 给定操作次数内使剩余元素的或值最小

算法可以发掘本质,如:

一,若干师傅和徒弟互有好感,有好感的师徒可以结对学习。师傅和徒弟都只能参加一个对子。如何让对子最多。

二,有无限多1X2和2X1的骨牌,某个棋盘若干格子坏了,如何在没有坏的格子放足够多骨牌。

三,某个单色图,1表示前前景,0表示后景色。每次操作可以将一个1,变成0。如何在最少得操作情况下,使得没有两个1相邻(四连通)。

四,若干路人,有些人是熟人,如何选出最多的人参加实验。为了避免熟人影响实验的效果,参加的人不能是熟人。

一二是二分图的最大匹配,三是二分图的最小点覆盖,四是二分图最大独立集。 而这三者是等效问题。

本文涉及知识点

位运算 试填法

LeetCode 3022. 给定操作次数内使剩余元素的或值最小

给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。

一次操作中,你可以选择 nums 中满足 0 <= i < nums.length - 1 的一个下标 i ,并将 nums[i] 和 nums[i + 1] 替换为数字 nums[i] & nums[i + 1] ,其中 & 表示按位 AND 操作。

请你返回 至多 k 次操作以内,使 nums 中所有剩余元素按位 OR 结果的 最小值

示例 1:

输入:nums = [3,5,3,2,7], k = 2

输出:3

解释:执行以下操作:

  1. 将 nums[0] 和 nums[1] 替换为 (nums[0] & nums[1]) ,得到 nums 为 [1,3,2,7] 。
  2. 将 nums[2] 和 nums[3] 替换为 (nums[2] & nums[3]) ,得到 nums 为 [1,3,2] 。
    最终数组的按位或值为 3 。
    3 是 k 次操作以内,可以得到的剩余元素的最小按位或值。
    示例 2:
    输入:nums = [7,3,15,14,2,8], k = 4
    输出:2
    解释:执行以下操作:
  3. 将 nums[0] 和 nums[1] 替换为 (nums[0] & nums[1]) ,得到 nums 为 [3,15,14,2,8] 。
  4. 将 nums[0] 和 nums[1] 替换为 (nums[0] & nums[1]) ,得到 nums 为 [3,14,2,8] 。
  5. 将 nums[0] 和 nums[1] 替换为 (nums[0] & nums[1]) ,得到 nums 为 [2,2,8] 。
  6. 将 nums[1] 和 nums[2] 替换为 (nums[1] & nums[2]) ,得到 nums 为 [2,0] 。
    最终数组的按位或值为 2 。
    2 是 k 次操作以内,可以得到的剩余元素的最小按位或值。
    示例 3:
    输入:nums = [10,7,10,3,9,14,9,4], k = 1
    输出:15
    解释:不执行任何操作,nums 的按位或值为 15 。
    15 是 k 次操作以内,可以得到的剩余元素的最小按位或值。
    提示:
    1 <= nums.length <= 105
    0 <= nums[i] < 230
    0 <= k < nums.length

试填法

image.png

假定我们能消除 iRemove,其中iRemvoe&iHas == iHas。

最终结果 iHas - iRemove。

iCanMove = ~iHas。

如果iCanMove的最高位i能消除,则 :

iCanMove -= (1 << i )

iRemove += (1 << i )

否则:

iCanMove -= (1 << i )

假定新的最高位i1,则iTest = iRemove + (1 <<i1)

如果iTest能消除:

iCanMove -= (1 << i )

iRemove = iTest

否则:

iCanMove -= (1 << i )

直到 iCanMove为0。可以不修改iCanMove,直接枚举iCanMove。也可以不需要iCanMove,直接i=29 to 0 ,然后判断(1 << i ) & iHas 是否为真。

计算消除iTest需要的次数

image.png

第一种情况:从i1到i2消除。

第二种情况:无法消除

第三种情况:消除[i1,i2]及左侧或右侧的元素。

第一种情况可以继续细分:如果[i1,i3]可以消除,则i3-i1次消掉,[i3+!,i2]下次再消。

代码

核心代码

class Solution {
public:
  int minOrAfterOperations(vector<int>& nums, int k) {
    for (const auto& n : nums) {
      m_iHas |= n;
    }
    int iRemove = 0;
    for (int i = 29; i >= 0; i--) {
      if (m_iHas & (1 << i)) {
        if (Need(nums, iRemove + (1 << i)) <= k) {
          iRemove += (1 << i);
        }
      }
    }
    return m_iHas - iRemove;
  }
  int Need(const vector<int>& nums, int iTest) {
    int iAnd = iTest;
    int iCnt = 0;
    int iNeed = 0;
    for (int i = 0; i < nums.size(); i++)
    {
      if (nums[i] & iTest) {
        iCnt++;
        iAnd &= nums[i];
        if (0 == iAnd) {
          iNeed += iCnt - 1;
          iCnt = 0;
          iAnd = iTest;
        }
      }
      else
      {
        iNeed += iCnt - 1 + (0 != iAnd);
        iCnt = 0;
        iAnd = iTest;
      }
    }
    if ((nums.size() == iCnt) && (iAnd)) {
      return 1'000'000'000;
    }
    iNeed += iCnt - 1 + (0 != iAnd);
    return iNeed;
  }
  int m_iHas = 0;
};

测试用例

template<class T>
void Assert(const T& t1, const T& t2)
{
  assert(t1 == t2);
}
template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{
  if (v1.size() != v2.size())
  {
    assert(false);
    return;
  }
  for (int i = 0; i < v1.size(); i++)
  {
    Assert(v1[i], v2[i]);
  }
}
int main()
{ 
  vector<int> nums;
  int k;
  {
    Solution sln;
    nums = { 3, 5, 3, 2, 7 }, k = 2;
    auto res = sln.minOrAfterOperations(nums, k);
    Assert(3, res);
  }
  {
    Solution sln;
    nums = { 7,3,15,14,2,8 }, k = 4;
    auto res = sln.minOrAfterOperations(nums, k);
    Assert(2, res);
  }
  {
    Solution sln;
    nums = { 10,7,10,3,9,14,9,4 }, k = 1;
    auto res = sln.minOrAfterOperations(nums, k);
    Assert(15, res);
  }
  {
    Solution sln;
    nums = { 37,6,46,32,23 }, k = 3;
    auto res = sln.minOrAfterOperations(nums, k);
    Assert(4, res);
  }
}

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。

https://edu.csdn.net/course/detail/38771

如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程

https://edu.csdn.net/lecturer/6176

相关下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版

https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17

或者 操作系统:win10 开发环境: VS2022 C++17

如无特殊说明,本算法用**C++**实现。

相关文章
|
开发工具 Android开发
Mac 安卓(Android) 配置adb路径
Mac 安卓(Android) 配置adb路径
1372 0
|
分布式计算 JavaScript 前端开发
JS中数组22种常用API总结,slice、splice、map、reduce、shift、filter、indexOf......
JS中数组22种常用API总结,slice、splice、map、reduce、shift、filter、indexOf......
431 0
遇到ffmpeg错误:non monotonically increasing dts to muxer in stream
遇到ffmpeg错误:non monotonically increasing dts to muxer in stream
1955 0
|
7月前
|
NoSQL MongoDB 数据库
数据库数据恢复——MongoDB数据库服务无法启动的数据恢复案例
MongoDB数据库数据恢复环境: 一台Windows Server操作系统虚拟机上部署MongoDB数据库。 MongoDB数据库故障: 管理员在未关闭MongoDB服务的情况下拷贝数据库文件。将MongoDB数据库文件拷贝到其他分区后,对MongoDB数据库所在原分区进行了格式化操作。格式化完成后将数据库文件拷回原分区,并重新启动MongoDB服务。发现服务无法启动并报错。
|
存储 算法 应用服务中间件
Tomcat如何配置JKS证书?
【10月更文挑战第2天】Tomcat如何配置JKS证书?
1079 4
|
10月前
|
机器学习/深度学习 编解码 vr&ar
NeurIPS 2024最佳论文,扩散模型的创新替代:基于多尺度预测的视觉自回归架构
本文详细解读NeurIPS 2024最佳论文《视觉自回归建模:基于下一尺度预测的可扩展图像生成》。该研究提出VAR模型,通过多尺度token图和VAR Transformer结构,实现高效、高质量的图像生成,解决了传统自回归模型在二维结构信息、泛化能力和计算效率上的局限。实验表明,VAR在图像质量和速度上超越现有扩散模型,并展示出良好的扩展性和零样本泛化能力。未来研究将聚焦于文本引导生成和视频生成等方向。
1030 8
NeurIPS 2024最佳论文,扩散模型的创新替代:基于多尺度预测的视觉自回归架构
|
存储 安全 物联网
探索未来网络:物联网安全的挑战与对策
本文深入探讨了物联网(IoT)技术的基本概念、发展现状以及面临的主要安全挑战,并提出了相应的解决策略。通过对当前物联网设备的安全漏洞和攻击手段的分析,文章强调了加强设备认证、数据加密和隐私保护等措施的重要性。同时,呼吁业界共同努力,制定统一的安全标准和规范,以促进物联网技术的健康发展。
|
Java 编译器 Android开发
Kotlin教程笔记(28) -Kotlin 与 Java 混编
Kotlin教程笔记(28) -Kotlin 与 Java 混编
181 2
|
11月前
|
机器学习/深度学习 数据采集 搜索推荐
使用Python实现深度学习模型:智能食品消费行为预测
使用Python实现深度学习模型:智能食品消费行为预测
276 8
|
存储 算法 关系型数据库
TDengine 3.3.0.0 发布:图形化管理工具、复合主键等10+ 功能更新
在涛思数据研发团队的努力下,TDengine 3.3.0.0 版本终于和大家见面了。这一版本中,我们引入了多项革新功能和性能优化,力求在为用户提供极致体验的同时,不断推动技术的前沿。
348 0