C++算法:最少翻转操作数

题目

给你一个整数 n 和一个在范围 [0, n - 1] 以内的整数 p ,它们表示一个长度为 n 且下标从 0 开始的数组 arr ,数组中除了下标为 p 处是 1 以外,其他所有数都是 0 。
同时给你一个整数数组 banned ,它包含数组中的一些位置。banned 中第 i 个位置表示 arr[banned[i]] = 0 ,题目保证 banned[i] != p 。
你可以对 arr 进行 若干次 操作。一次操作中,你选择大小为 k 的一个 子数组 ,并将它 翻转 。在任何一次翻转操作后,你都需要确保 arr 中唯一的 1 不会到达任何 banned 中的位置。换句话说,arr[banned[i]] 始终 保持 0 。
请你返回一个数组 ans ,对于 [0, n - 1] 之间的任意下标 i ,ans[i] 是将 1 放到位置 i 处的 最少 翻转操作次数,如果无法放到位置 i 处,此数为 -1 。
子数组 指的是一个数组里一段连续 非空 的元素序列。
对于所有的 i ,ans[i] 相互之间独立计算。
将一个数组中的元素 翻转 指的是将数组中的值变成 相反顺序 。
1 <= n <= 105
0 <= p <= n - 1
0 <= banned.length <= n - 1
0 <= banned[i] <= n - 1
1 <= k <= n 
banned[i] != p
banned 中的值 互不相同


用例分析

不考虑banned。假定1在i出,翻转[i,i+k),则1到了i+k-1处。翻转[j,j+k-1],翻转之前,i距离子数组的开始i-j,那么翻转后,1的距离子数组的结束i-j,即:j+k-1-(i-j)= k+2*j-i-1=k-i-1+2*j
j的取值范围为:[i-k+1,i],同时[0,n-k]
n=5,k=4

n=5,k=4

10000

00010

k-i-1=3新位置为:3+0=3

01000

00100 00001

k-i-1=2新位置为:2+0=2 2+2=4

00100

01000 00010

k-i-1=1新位置为:1+0=1 1+2=3

00010

10000 00100

k-i-1=0新位置为:0+0=0 0+2=2

00001

01000

k-i-1=-1新位置为:  -1+2=1

注意
p已经处理。


核心代码

class Solution {
public:
  vector<int> minReverseOperations(int n, int p, vector<int>& banned, int k) {
    vector<int> vRet(n, -1);
    vRet[p] = 0;
    queue<int> que;
    que.emplace(p);
    set<int> set0, set1;
    for (int i = 0; i < n; i++)
    {
      if (i & 1)
      {
        set1.emplace(i);
      }
      else
      {
        set0.emplace(i);
      }
    }
    for (const auto& b : banned)
    {
      set0.erase(b);
      set1.erase(b);
    }
    set0.erase(p);
    set1.erase(p);
    while (que.size())
    {
      const auto cur = que.front();
      que.pop();
      const int j0 = max(0, cur - k + 1);
      const int j1 = min(cur, n - k);
      const int next0 = k - cur - 1 + 2 * j0;
      const int next1 = k - cur - 1 + 2 * j1;
      auto& setCur = (next0 & 1) ? set1 : set0;
      auto it0 = setCur.lower_bound(next0);
      auto it1 = setCur.upper_bound(next1 + 1);
      for (auto it = it0; it != it1; ++it)
      {
        vRet[*it] = vRet[cur] + 1;
        que.emplace(*it);
      }
      setCur.erase(it0, it1);
    }
    return vRet;
  }
};

时间复杂度

用广度优先实现,入队n次。每次出队时间复杂度O(k),总时间复杂度O(kn)。出队的时间复杂度可以优化。每次出队是连续的奇数或偶数,我们可以用两个std::set分别纪录未处理的奇数和偶数。未处理分两种情况:已经处理,被禁止。每次出队只需要查询两次,时间复杂度O(logn)。故总时间复杂度为O(nlogn)。

并集查找

如果i是偶数,vDo[i]记录需要处理的偶数中,大于等于i,且最小的数。奇数类似。以n等于8为例,只分析偶数,奇数类似。初始{0,2,4,6}

{0,2,4,6}

处理0{2,2,4,6} 处理2{0,4,4,6} 处理4{0,2,6,6} 处理6{0,2,4,MAX}

2,2,4,6

处理0{2,4,4,6}处理2{2,4,4,6} 处理4{2,2,6,6}处理6{2,2,6,MAX}

{2,4,4,6}

处理0{4,4,6,6},处理2{2,4,6,6}处理4{2,4,6,6}处理6{2,4,4,MAX}

非常类似并集查找,由于i一定小于vDo[i],所以不会有环。可以直接使用封装好的有向图的并集查找。

代码

class Solution {
public:
  vector<int> minReverseOperations(int n, int p, vector<int>& banned, int k) {
    vector<int> vRet(n, -1);
    vRet[p] = 0;
    queue<int> que;
    que.emplace(p);
    vector<int> vNext(n);
    for (int i = 0; i < n; i++)
    {
      vNext[i] = i;
    }   
    const int iMax = 1000 * 1000;
    auto HasDo = [&](int b)
    {
      vNext[b] = (b + 2 < n) ? vNext[b + 2] : iMax;
    };
    
    banned.emplace_back(p);
    for (const auto& b : banned)
    {
      HasDo(b);
    }
    while (que.size())
    {
      const auto cur = que.front();
      que.pop();
      const int j0 = max(0, cur - k + 1);
      const int j1 = min(cur, n - k);
       int next0 = k - cur - 1 + 2 * j0;
      const int next1 = k - cur - 1 + 2 * j1;
      
      for (next0 = GetNext(vNext, next0, iMax); next0 <= next1;)
      {
        vRet[next0] = vRet[cur] + 1;
        HasDo(next0);
        que.emplace(next0);
        next0 = GetNext(vNext, next0, iMax);
      }

    }
    return vRet;
  }
  int GetNext(vector<int>& vNext,const int b,const int iMax)
  {
    if ((iMax == b) || (b == vNext[b]))
    {
      return b;
    };
    return vNext[b]= GetNext(vNext, vNext[b],iMax);
  };
};
 

 其它

视频课程

如果你觉得复杂,想从简单的算法开始,可以学习我的视频课程。
https://edu.csdn.net/course/detail/38771
我的其它课程
https://edu.csdn.net/lecturer/6176

 测试环境

win7 VS2019 C++17

  相关下载

doc版文档,**排版好**
https://download.csdn.net/download/he_zhidan/88348653
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/101312.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

金融信创黄金三年:小程序生态+跨端技术框架构建

小程序应用场景生态的发展&#xff0c;受益于开源技术的发展&#xff0c;以及响应快速开发的实际业务需求&#xff0c;一些跨端框架如&#xff1a;Electron、wxPython、FinClip、Tauri、Flutter等发展也非常迅速&#xff0c;小程序生态跨端技术框架&#xff0c;不仅能满足自有超…

生活中的视音频技术

生活中的视音频技术 平时我们打开电脑中自己存电影的目录的话&#xff0c;一般都会如下图所示&#xff0c;一大堆五花八门的电影。&#xff08;其实专业的影视爱好者一概会把影视文件分门别类的&#xff0c;但我比较懒&#xff0c;一股脑把电影放在了一起&#xff09; 因为下载…

10.9~10.10

触发器方程 触发器之间的转化 触发器需要输入和输出 对于D触发器&#xff0c;只需要D信号的输入 对于jk触发器&#xff0c;至少需要原状态的与非信号&#xff0c;并且需要j,k信号 假如确定y2,则D触发器需要什么D信号&#xff0c;是需要卡诺图确定 j-k触发器需要什么jk信号…

经典循环命题:百钱百鸡

翁五钱一只&#xff0c;母三钱&#xff0c;小鸡三只一钱&#xff1b;百钱百鸡百鸡花百钱。 (本笔记适合能熟练应用for循环、会使if条件分支语句、能格式化字符输出的 coder 翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a…

数字化产业研究报告

一、当前个人认知的数字化 &#xff08;一&#xff09;信息化 1、软件分类标&#xff1a;GB_T 36475-2018_2957 2、信息化程度&#xff1a;诺兰模型- 初始、扩展、控制、统一、数据管理、成熟 3、从信息系统的发展和特点来看&#xff0c;可分为下面几类&#xff1a; &…

Android 蓝牙设备类型判断代码介绍

Android 蓝牙设备类型判断 一、前言 Android 蓝牙设备有各种类型&#xff0c;比如蓝牙手机&#xff0c;蓝牙耳机&#xff0c;蓝牙手环&#xff0c;蓝牙鼠标键盘&#xff0c;蓝牙玩具&#xff0c;健康检测设备等等。 有些场景需要具体区分就需要进行判断了。一个是扫描到后图…

【数据结构】二叉树的链式结构及实现

目录 1. 前置说明 2. 二叉树的遍历 2.1 前序、中序以及后序遍历 2.2 层序遍历 3. 节点个数及高度等 4. 二叉树的创建和销毁 1. 前置说明 在学习二叉树的基本操作前&#xff0c;需先要创建一棵二叉树&#xff0c;然后才能学习其相关的基本操作。由于现在大家对二叉树结构…

区块链跨链技术

区块链跨链技术 背景 近年来&#xff0c;随着区块链技术的不断发展&#xff0c;区块链的应用场景逐渐从最初的加密货币领域扩展到金融、物流、医疗、公共服务等各个领域。随着区块链的应用场景不断增多&#xff0c;区块链的“数据孤岛”问题日益突出&#xff0c;不同场景下的…

QT编程,QMainWindow、事件

目录 1、QMainWindow 2、事件 1、QMainWindow QMenuBar&#xff1a;菜单栏 QMenu: 菜单 QAction: 动作 QToolBar: 工具栏 QStatusBar: 状态栏 setWindowTitle("主窗口"); //: 前缀 文件名 setWindowIcon(QIcon(":/mw_images/10.png")); resize(640, 4…

生信学院|10月13日《SOLIDWORKS参数化应用——DriveWorksXpress》

课程主题&#xff1a;SOLIDWORKS参数化应用——DriveWorksXpress 课程时间&#xff1a;2023年10月13日 14:00-14:30 主讲人&#xff1a;温晓露 生信科技 售后服务工程师 1、DriveWorks的作用 2、用 DriveWorksXpress 自动化您的设计过程 3、Drive Works Xpress最佳做法 4…

软件测试概率性问题

软件测试中常见的一个问题就是概率性问题&#xff0c;概率性问题无论对软件测试人员还是对开发人员而言都是比较头疼的一个问题。这种概率性问题在测试中该如何处理呢? 首先&#xff0c;概率性问题也是问题&#xff0c;这种我们千万不能一笑而过&#xff0c;在这种情况下测试…

如何将jpg转化为png?

如何将jpg转化为png&#xff1f;可能有的小伙伴就会疑惑了&#xff0c;jpg和png都是图片常用的一种格式&#xff0c;为什么要进行格式的更改呢&#xff1f;那是因为PNG格式具有更好的图片质量和更少的失真。JPG&#xff08;或JPEG&#xff09;格式的图片通常是压缩过的&#xf…

Mall脚手架总结(二) —— SpringData操作Elasticsearch

前言 万字长文带你弄清楚SpringData中的Elasticsearch操作以及在脚手架里接口的结构关系&#xff01;经过前面鉴证授权的整合&#xff0c;荔枝开始熟悉项目的学习的方法了&#xff0c;虽然脚手架中的内容比较简单&#xff0c;但是把边角的知识点全部扫到还是比较花时间的尤其是…

C#和JS交互之Microsoft.ClearScript.V8(V8引擎)

之前测试了很多JS引擎&#xff0c;都只支持es5语法&#xff0c;不支持执行es6&#xff0c;测试了下微软的V8反正能跑通&#xff0c;应该是支持的。还得是微软呀。 如图&#xff1a;安装相关包&#xff1a; 这是参考的官方V8代码 using Microsoft.ClearScript.JavaScript; us…

当下测试行业中UI自动化面临的难点及如何解决

经常有人会问&#xff0c;什么样的项目才适合进行UI自动化测试呢&#xff1f;UI自动化测试相当于模拟手工测试&#xff0c;通过程序去操作页面上的控件。而在实际测试过程中&#xff0c;经常会遇到无法找到控件&#xff0c;或者因控件定义变更而带来的维护成本等问题。 哪些场…

jvm--执行引擎

文章目录 1. 执行引擎的工作流程2. 解释器、JIT及时编译器3. 热点代码及探测技术4. HotSpotVM 中 JIT 分类 执行引擎属于 JVM 的下层&#xff0c;里面包括解释器、及时编译器、垃圾回收器 JVM 的主要任务是负责 装载字节码到其内部&#xff0c;但字节码并不能够直接运行在操作…

Zookeeper分布式一致性协议ZAB源码剖析

文章目录 1、ZAB协议介绍2、消息广播 1、ZAB协议介绍 ZAB 协议全称&#xff1a;Zookeeper Atomic Broadcast&#xff08;Zookeeper 原子广播协议&#xff09;。 Zookeeper 是一个为分布式应用提供高效且可靠的分布式协调服务。在解决分布式一致性方面&#xff0c;Zookeeper 并…

Docker 的网络与数据管理

Docker 网络实现原理 Docker使用Linux桥接&#xff0c;在宿主机虚拟一个Docker容器网桥(docker0)&#xff0c;Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址&#xff0c;称为Container-IP&#xff0c;同时Docker网桥是每个容器的默认网关。因为在同一宿主机…

栈的模拟实现(Java)

目录 1、 栈的概念2、栈的使用3、栈的模拟实现 1、 栈的概念 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last I…

Linux 部署1Panel 现代化运维管理面板进行公网远程访问

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《速学数据结构》 《C语言进阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 前言1. Linux 安装1Panel2. 安装cpolar内网穿透2.1 使用一键脚本安装命令 2.2向系统添加服务2.3 启动cpolar服务…