按最少次数开关点亮所有灯

简介: 按最少次数开关点亮所有灯

题目

给定一个数组arr,长度为N,arr中的值不是0就是1。arr[i]表示第i栈灯的状态,0代表灭灯,1代表亮灯
每一栈灯都有开关,但是按下i号灯的开关,会同时改变i-1、i、i+1栈灯的状态
问题一:如果N栈灯排成一条直线,请问最少按下多少次开关?
i为中间位置时,i号灯的开关能影响i-1、i和i+1
0号灯的开关只能影响0和1位置的灯
N-1号灯的开关只能影响N-2和N-1位置的灯

解题思路

递归:从左到右模型
image.png
递归函数f(nextIndex,preStatus,curStatus)
nextIndex:当前位置的下一个位置,即当前来到的位置为nextIndex-1
preStatus:当前位置的前一个位置的状态
curStatus:当前位置的状态
潜台词0-i-1位置的灯是全亮的
image.png
1)0位置按了开关.去1位置做选择,调用f(2,1,0)
2)0位置没按开关,调用f(2,0,1)
f返回值:
使之前做的决定都已经体现在参数上了,i位置怎么做决定能让整体的灯全亮,而且最少的开关数

递归代码

    public static int noLoopMinStep1(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        if (arr.length == 1) {
            return arr[0] ^ 1;
        }
        if (arr.length == 2) {
            return arr[0] != arr[1] ? Integer.MAX_VALUE : (arr[0] ^ 1);
        }
        // 不变0位置的状态
        int p1 = process1(arr, 2, arr[0], arr[1]);
        // 改变0位置的状态
        int p2 = process1(arr, 2, arr[0] ^ 1, arr[1] ^ 1);
        if (p2 != Integer.MAX_VALUE) {
            p2++;
        }
        return Math.min(p1, p2);
    }

    // 当前在哪个位置上,做选择,nextIndex - 1 = cur ,当前!
    // cur - 1 preStatus
    // cur  curStatus
    // 0....cur-2  全亮的!
    public static int process1(int[] arr, int nextIndex, int preStatus, int curStatus) {
        if (nextIndex == arr.length) { // 当前来到最后一个开关的位置
            return preStatus != curStatus ? (Integer.MAX_VALUE) : (curStatus ^ 1);
        }
        // 没到最后一个按钮呢!
        // i < arr.length
        if (preStatus == 0) { // 一定要改变
            curStatus ^= 1;
            int cur = arr[nextIndex] ^ 1;
            int next = process1(arr, nextIndex + 1, curStatus, cur);
            return next == Integer.MAX_VALUE ? next : (next + 1);
        } else { // 一定不能改变
            return process1(arr, nextIndex + 1, curStatus, arr[nextIndex]);
        }
    }

递归改迭代

你当初0位置,如果按下了按钮,你是一条唯一的路径,一直决策到N没分叉
如果你0位置没有按下按钮,它依然是一条单独的分支, 走到N没有分叉

    public static int noLoopMinStep(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        if (arr.length == 1) {
            return arr[0] == 1 ? 0 : 1;
        }
        if (arr.length == 2) {
            return arr[0] != arr[1] ? Integer.MAX_VALUE : (arr[0] ^ 1);
        }
        int p1 = traceNoLoop(arr, arr[0], arr[1]);
        int p2 = traceNoLoop(arr, arr[0] ^ 1, arr[1] ^ 1);
        p2 = (p2 == Integer.MAX_VALUE) ? p2 : (p2 + 1);
        return Math.min(p1, p2);
    }

进阶

如果N栈灯排成一个圈,请问最少按下多少次开关,能让灯都亮起来
i为中间位置时,i号灯的开关能影响i-1、i和i+1
0号灯的开关能影响N-1、0和1位置的灯
N-1号灯的开关能影响N-2、N-1和0位置的灯

题解

在无环问题中的递归函数增加两个参数
firstStatus:可能当前来到了i位置,当初0位置的状态是firststatus,因为来到最后一盏灯时是可以改变零的
endStatus:可能当前来到了i位置,当初0位置做完决定最后一个状态是endStatus
来到一个位置,它后面的位置是i, 一个普遍位置,告诉你当初0位置的状态firstStatus,最后一个位置的状态endStatus,
把这两个东西作为可变参数告诉你,你给我做决策
接下来你的下一个位置,你之前的状态跟你当前的状态跟刚才的函数一样

image.png
i:当前在i-1位置,是下一个位置
p:当前在i-1位置, i-2位置灯的状态
c:当前i-1位置,i-1位置灯的状态
fs: 0位置灯的状态
endS: N-1位置灯的状态
image.png
主函数调用4种分支
1)0按开关,1位置也按开关
2)0按开关,1位置不按开关
3)0不按开关,1位置不按开关
4)0不按开关,1位置按开关
image.png
来到最后一个位置,现在知道0位置的状态,最后一盖灯的状态和前一盖灯的状态,三个状态都要一样,否则没法解决了

一个普遍位置的i,它将遵循的原则:
它前面的状态如果是亮的,它就不按
它前面的位置,如果是灭了,它一定要按
这是一个普适性质的i,但这个普适性在1位置就不能适用这个原则了。
客观上来讲1位置前面这个0位置不管是亮还是灭都可以,因为0位置还能被最后一个N-1位置搬回来

代码

    public static int loopMinStep1(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        if (arr.length == 1) {
            return arr[0] == 1 ? 0 : 1;
        }
        if (arr.length == 2) {
            return arr[0] != arr[1] ? Integer.MAX_VALUE : (arr[0] ^ 1);
        }
        if (arr.length == 3) {
            return (arr[0] != arr[1] || arr[0] != arr[2]) ? Integer.MAX_VALUE : (arr[0] ^ 1);
        }
        // 0不变,1不变
        int p1 = process2(arr, 3, arr[1], arr[2], arr[arr.length - 1], arr[0]);
        // 0改变,1不变
        int p2 = process2(arr, 3, arr[1] ^ 1, arr[2], arr[arr.length - 1] ^ 1, arr[0] ^ 1);
        // 0不变,1改变
        int p3 = process2(arr, 3, arr[1] ^ 1, arr[2] ^ 1, arr[arr.length - 1], arr[0] ^ 1);
        // 0改变,1改变
        int p4 = process2(arr, 3, arr[1], arr[2] ^ 1, arr[arr.length - 1] ^ 1, arr[0]);
        p2 = p2 != Integer.MAX_VALUE ? (p2 + 1) : p2;
        p3 = p3 != Integer.MAX_VALUE ? (p3 + 1) : p3;
        p4 = p4 != Integer.MAX_VALUE ? (p4 + 2) : p4;
        return Math.min(Math.min(p1, p2), Math.min(p3, p4));
    }

    
    // 下一个位置是,nextIndex
    // 当前位置是,nextIndex - 1 -> curIndex
    // 上一个位置是, nextIndex - 2 -> preIndex   preStatus
    // 当前位置是,nextIndex - 1, curStatus
    // endStatus, N-1位置的状态
    // firstStatus, 0位置的状态
    // 返回,让所有灯都亮,至少按下几次按钮
    
    // 当前来到的位置(nextIndex - 1),一定不能是1!至少从2开始
    // nextIndex >= 3
    public static int process2(int[] arr, 
            int nextIndex, int preStatus, int curStatus, 
            int endStatus, int firstStatus) {
        
        if (nextIndex == arr.length) { // 最后一按钮!
            return (endStatus != firstStatus || endStatus != preStatus) ? Integer.MAX_VALUE : (endStatus ^ 1);
        }
        // 当前位置,nextIndex - 1
        // 当前的状态,叫curStatus
        // 如果不按下按钮,下一步的preStatus, curStatus
        // 如果按下按钮,下一步的preStatus, curStatus ^ 1
        // 如果不按下按钮,下一步的curStatus, arr[nextIndex]
        // 如果按下按钮,下一步的curStatus, arr[nextIndex] ^ 1
        int noNextPreStatus = 0;
        int yesNextPreStatus = 0;
        int noNextCurStatus =0;
        int yesNextCurStatus = 0;
        int noEndStatus = 0;
        int yesEndStatus = 0;
        if(nextIndex < arr.length - 1) {// 当前没来到N-2位置
             noNextPreStatus = curStatus;
             yesNextPreStatus = curStatus ^ 1;
             noNextCurStatus = arr[nextIndex];
             yesNextCurStatus = arr[nextIndex] ^ 1;
        } else if(nextIndex == arr.length - 1) {// 当前来到的就是N-2位置
            noNextPreStatus = curStatus;
            yesNextPreStatus = curStatus ^ 1;
            noNextCurStatus = endStatus;
            yesNextCurStatus = endStatus ^ 1;
            noEndStatus = endStatus;
            yesEndStatus = endStatus ^ 1;
        }
        if(preStatus == 0) {
            int next = process2(arr, nextIndex + 1, yesNextPreStatus, yesNextCurStatus,
                    nextIndex == arr.length - 1 ? yesEndStatus : endStatus, firstStatus);
            return next == Integer.MAX_VALUE ? next : (next + 1);
        }else {
            return process2(arr, nextIndex + 1, noNextPreStatus, noNextCurStatus, 
                    nextIndex == arr.length - 1 ? noEndStatus : endStatus, firstStatus);
                    
        }
//        int curStay = (nextIndex == arr.length - 1) ? endStatus : arr[nextIndex];
//        int curChange = (nextIndex == arr.length - 1) ? (endStatus ^ 1) : (arr[nextIndex] ^ 1);
//        int endChange = (nextIndex == arr.length - 1) ? curChange : endStatus;
//        if (preStatus == 0) {
//            int next = process2(arr, nextIndex + 1, curStatus ^ 1, curChange, endChange, firstStatus);
//            return next == Integer.MAX_VALUE ? next : (next + 1);
//        } else {
//            return process2(arr, nextIndex + 1, curStatus, curStay, endStatus, firstStatus);
//        }
    }

迭代

    public static int traceLoop(int[] arr, int preStatus, int curStatus, int endStatus, int firstStatus) {
        int i = 3;
        int op = 0;
        while (i < arr.length - 1) {
            if (preStatus == 0) {
                op++;
                preStatus = curStatus ^ 1;
                curStatus = (arr[i++] ^ 1);
            } else {
                preStatus = curStatus;
                curStatus = arr[i++];
            }
        }
        if (preStatus == 0) {
            op++;
            preStatus = curStatus ^ 1;
            endStatus ^= 1;
            curStatus = endStatus;
        } else {
            preStatus = curStatus;
            curStatus = endStatus;
        }
        return (endStatus != firstStatus || endStatus != preStatus) ? Integer.MAX_VALUE : (op + (endStatus ^ 1));
    }
相关文章
|
数据采集 存储 人工智能
TripoSR开源!从单个图像快速生成 3D 对象!(附魔搭社区推理实战教程)
近期,VAST团队和Stability AI团队合作发布了TripoSR,可在一秒内从单个图像生成高质量3D对象。
|
安全 项目管理
一文搞懂需求流程规范的制定方法和落地技巧
随着业务和产品的发展、团队的不断扩大,很多团队都不可避免的会遇到需求流程混乱的问题。虽然有的团队也编写了一些“需求流程规范”的文档,但最终却流于纸面,难以在团队真正落地。如何科学制定并有效落实需求管理规范呢?对此,云效产品经理陈逊进行了非常详细的直播分享,本文是他经验的文字总结。
103584 19
|
12月前
|
分布式计算 大数据 Java
大数据-86 Spark 集群 WordCount 用 Scala & Java 调用Spark 编译并打包上传运行 梦开始的地方
大数据-86 Spark 集群 WordCount 用 Scala & Java 调用Spark 编译并打包上传运行 梦开始的地方
203 1
大数据-86 Spark 集群 WordCount 用 Scala & Java 调用Spark 编译并打包上传运行 梦开始的地方
|
9月前
|
C语言
【C语言程序设计——入门】基本数据类型与表达式(头歌实践教学平台习题)【合集】
这份文档详细介绍了编程任务的多个关卡,涵盖C语言的基础知识和应用。主要内容包括: 1. **目录**:列出所有关卡,如`print函数操作`、`转义字符使用`、`数的向上取整`等。 2. **各关卡的任务描述**:明确每关的具体编程任务,例如使用`printf`函数输出特定字符串、实现向上取整功能等。 3. **相关知识**:提供完成任务所需的背景知识,如格式化输出、算术运算符、关系运算符等。 4. **编程要求**:给出具体的代码编写提示。 5. **测试说明**:包含预期输入输出,帮助验证程序正确性。 6. 文档通过逐步引导学习者掌握C语言的基本语法和常用函数,适合初学者练习编程技能。
229 1
|
机器学习/深度学习 自然语言处理 物联网
Chronos: 将时间序列作为一种语言进行学习
Chronos框架预训练时间序列模型,将序列值转为Transformer模型的tokens。通过缩放、量化处理,模型在合成及公共数据集上训练,参数量20M至710M不等。优于传统和深度学习模型,展示出色零样本预测性能。使用分类交叉熵损失,支持多模态输出分布学习。数据增强策略包括TSMix和KernelSynth。实验显示大型Chronos模型在概率和点预测上超越多种基线,且微调小型模型表现优异。虽然推理速度较慢,但其通用性简化了预测流程。论文探讨了优化潜力和未来研究方向。
798 3
|
机器学习/深度学习 算法
深度学习之因果发现算法
基于深度学习的因果发现算法是一个旨在从复杂数据中自动挖掘变量之间潜在因果关系的研究领域。它结合了传统因果推理方法与深度学习的强大特征提取能力,帮助应对高维、非线性数据中的因果结构发现。
851 9
|
测试技术 计算机视觉
斯坦福新研究提升大模型长视频理解能力
【2月更文挑战第29天】斯坦福大学研究团队开发的VideoAgent系统在长视频理解上取得突破,提升了大型语言模型处理视频内容的能力。该系统通过模拟人类认知过程,以高效(平均8.4帧)实现高准确率(54.1%和71.3%的零样本准确率),在EgoSchema和NExT-QA基准测试中超越现有最佳方法。VideoAgent借鉴人类观看视频的方式,迭代选择关键帧进行信息提取和推理,为长视频理解设定新标准。论文链接:[arxiv.org/pdf/2403.10517.pdf](https://arxiv.org/pdf/2403.10517.pdf)
424 1
斯坦福新研究提升大模型长视频理解能力
|
Go
Golang深入浅出之-信号(Signals)处理与优雅退出Go程序
【4月更文挑战第23天】在Go语言中,使用`os/signal`包处理信号对实现程序优雅退出和响应中断至关重要。本文介绍了如何注册信号处理器、处理常见问题和错误,以及提供代码示例。常见问题包括未捕获关键信号、信号处理不当导致程序崩溃和忽略清理逻辑。解决方案包括注册信号处理器(如`SIGINT`、`SIGTERM`)、保持信号处理器简洁和执行清理逻辑。理解并正确应用这些原则能增强Go程序的健壮性和可管理性。
590 1
|
移动开发 前端开发 JavaScript
原生求生记:揭秘UniApp的原生能力限制
原生求生记:揭秘UniApp的原生能力限制
|
存储 关系型数据库 MySQL
mysql mysqldump用法详解
mysql mysqldump用法详解
1032 0