【动态规划】最少按多少下开关使灯全亮

无环条件下

题目描述

给定一个数组arr,长度为N,arr中的值不是0就是1

  • arr[i]表示第i栈灯的状态,0代表灭灯,1代表亮灯
  • 每一栈灯都有开关,但是按下i号灯的开关,会同时改变i-1、i、i+2栈灯的状态 (改变状态是指灯由原来的灭变亮和由原来的亮变灭)
    问题一:
  • 如果N栈灯排成一条直线,请问最少按下多少次开关,能让灯都亮起来
  • 排成一条直线说明:
  • i为中间位置时,i号灯的开关能影响i-1、i和i+1
  • 0号灯的开关只能影响0和1位置的灯
  • N-1号灯的开关只能影响N-2和N-1位置的灯

解题思路

还是使用动态规划的思想,主要是递推数组的定义,因为一个开关可以影响3个开关.

所以我们要让该递推数组带上三个状态,如当前的位置是i,那么我们就要记录一下i+1位置下标,前一个的状态preState,当前的状态curState.

有了这三个状态,我们后面才方便去根据前一个位置的状态去推当前的状态,这也就是动态规划的一个最基本的思想,就是开辟多个参数来记录不同的状态

具体的变化过程:

如果preState的状态为0,那么说明我们一定要按下当前i位置的开关.
如果preState的状态为1,那么我们就不需要按下当前的i位置的开关

上面的就是递推公式,对于临界情况:

如果i到达了arr.length-1,也就是i+1到arr.length了,那么我们就要判断preState和curState的状态是否相同了.此时默认前面的状态都是1

如果不相同,那么就没有机会全亮了,直接返回Integer.MAX_VALUE
如果相同,那么如果状态为1,返回0;如果状态为0,返回1

递归版本

public int noLoopMinStep1(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=process(arr,2,arr[0],arr[1]);  
    int p2=process(arr,2,arr[0]^1,arr[1]);  
    if(p2!=Integer.MAX_VALUE)  
        p2++;  
    return Math.min(p1,p2);  
}  
public int process(int[] arr,int nextIndex,int preState,int curState){  
	//临界情况
    if(nextIndex==arr.length)  
        return preState!=curState?Integer.MAX_VALUE:preState^1;  
    if(preState==0){  
        curState^=1;  
        int cur=arr[nextIndex]^1;  
        int next=process(arr,nextIndex+1,curState,cur);  
        return next==Integer.MAX_VALUE?next:next+1;  
    }else{  
        return process(arr,nextIndex+1,curState,arr[nextIndex]);  
    }  
}

迭代版本

public int noLoopMinStep2(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=process2(arr,arr[0],arr[1]);  
    int p2=process2(arr,arr[0]^1,arr[1]);  
    if(p2!=Integer.MAX_VALUE)  
        p2++;  
    return Math.min(p1,p2);  
}  
private int process2(int[] arr, int preState, int curState) {  
    int i=2;  
    int count=0;  
    while(i<arr.length){  
        if(preState==0){  
            count++;  
            preState=curState^1;  
            curState=arr[i++]^1;  
        }else {  
            preState=curState;  
            curState=arr[i++];  
        }  
    }  
    return preState!=curState?Integer.MAX_VALUE:(count+preState^1);  
}

有环状态下

题目描述

和前面的题目一样,不同的是收尾相连是一个环.

解题思路

加上了环之后,我们最大的问题就是修改0位置的时候会影响尾,修改尾的时候会影响头.对于其他位置的元素和没环的时候是一致的.

递归版本

主函数:
我们从2下标开始,是因为想要在递归中避开firstIndex的影响.所以在主函数中单独的对0和1下标进行更改.因为只有0和1会影响到firstIndex的值,从2开始之后都不会影响到firstIndex的值

public int LoopMinStep(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[1]!=arr[2]?Integer.MAX_VALUE:arr[0]^1;  
    //0不变 1不变  
    int p1=processForLoop(arr,3,arr[1],arr[2],arr[arr.length-1],arr[0]);  
    //0不变 1改变  
    int p2=processForLoop(arr,3,arr[1]^1,arr[2]^1,arr[arr.length-1],arr[0]^1);  
    //0改变 1不变  
    int p3=processForLoop(arr,3,arr[1]^1,arr[2],arr[arr.length-1]^1,arr[0]^1);  
    //0改变 1改变  
    int p4=processForLoop(arr,3,arr[1],arr[2]^1,arr[arr.length-1]^1,arr[0]);  
    p1=p1==Integer.MAX_VALUE?Integer.MAX_VALUE:p1++;  
    p2=p2==Integer.MAX_VALUE?Integer.MAX_VALUE:p2++;  
    p1=p3==Integer.MAX_VALUE?Integer.MAX_VALUE:p3++;  
    p1=p4==Integer.MAX_VALUE?Integer.MAX_VALUE:p4++;  
    return Math.min(Math.min(p1,p2),Math.min(p3,p4));  
}

因为有环的原因,所以我们又新加了两个状态firstState和endState.
当我们到达arr.length-2下标时,也就是nextIndex=arr.length-1时,会对endState有影响,这一点注意就好了

public int processForLoop(int[] arr,int nextIndex,int preState,int curState,int endState,int firstState){  
    //临界情况
    if(nextIndex==arr.length)  
        return (preState!=curState||curState!=firstState)?Integer.MAX_VALUE:endState^1;  
    if(preState==0){  
        int next=processForLoop(arr,nextIndex+1,curState^1,  
                                nextIndex==arr.length-1?endState^1:arr[nextIndex]^1,  
                                nextIndex==arr.length-1?endState^1:endState,firstState);  
        return next==Integer.MAX_VALUE?Integer.MAX_VALUE:next+1;  
    }else {  
        return processForLoop(arr,nextIndex+1,curState,arr[nextIndex], endState,firstState);  
    }
}

迭代版本

还是从i=3开始,还是要注意i=arr.length-1的时候的endState的值

public static int processForLoop(int[] arr,int preState,int curState,int endState,int firstState){  
    int i=3;  
    int op=0;  
    while (i<arr.length-1){  
        if(preState==0){  
            preState=curState^1;  
            curState=arr[i++]^1;  
            op++;  
        }else{  
            preState=curState;  
            curState=arr[i++];  
        }  
    }  
    //i==arr.length-1,也就是当前的位置在arr.length-2  
    if(preState==0){  
        op++;  
        preState=curState^1;  
        endState^=1;  
        curState=endState;  
    }else{  
        preState=curState;  
        curState=endState;  
    }  
    //i==arr.length了  
    return (preState!=curState||curState!=firstState)?Integer.MAX_VALUE:(op+curState^1);  
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是示例代码: ```C #include "stm32f4xx.h" int main(void) { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能GPIOA时钟 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //设置按键所在引脚 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; //设置为输入模式 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //设置为上拉模式 GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); //使能GPIOB时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //设置LED所在引脚 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //设置为输出模式 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //设置为推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //设置输出速率为100MHz GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //设置为无上下拉 GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIOB while (1) { if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0) //检测按键是否按下 { GPIO_SetBits(GPIOB, GPIO_Pin_6); //点亮LED Delay(500); //延时500ms GPIO_ResetBits(GPIOB, GPIO_Pin_6); //熄灭LED } } } //延时函数 void Delay(__IO uint32_t nCount) { while(nCount--) { __NOP(); //空操作 } } ``` 这个代码使用了STM32的GPIO模块控制LED灯和按键。当检测到按键按下时,LED灯就会点亮,并延时500ms后熄灭。Delay()函数是一个简单的延时函数,用来模拟延时效果。请注意,这只是一个示例代码,实际使用中可能需要根据具体情况进行修改和优化。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值