熄灯问题的变种 状态锁问题

问题描述:3.特殊密码锁问题(熄灯问题)
有一种特殊的二进制密码锁,由n个相连的按钮组成(n<30),按钮有凹/凸两种状态,用手按按钮会改变其状态。
然而让人头疼的是,当你按一个按钮时,跟它相邻的两个按钮状态也会反转。当然,如果你按的是最左或者最右边的按钮,该按钮只会影响到跟它相邻的一个按钮。当前密码锁状态已知,需要解决的问题是,你至少需要按多少次按钮,才能将密码锁转变为所期望的目标状态。
输入:两行,给出两个由0、1组成的等长字符串,表示当前/目标密码锁状态,其中0代表凹,1代表凸。
输出:至少需要进行的按按钮操作次数,如果无法实现转变,则输出impossible。
样例输入
011
000
样例输出
1
样例输入
01111
00000
样例输出
impossible
例如八个按钮 00000000
按1后 11000000
按3后 10110000
按1后 01110000
这和八个按钮 00000000
只按一次3后 01110000
是完全相同的情况

解析:按照数学的方式来解这题是很复杂的,因为很多种不同的情况,也不能确定按几下才是最少的,就像是熄灯问题一样:前面的按钮状态是由后面一个按钮按下与否决定的,而不是由按下自身来改变状态。

我们只需要考虑是否按下第一个按钮。因为如果第一个按钮的状态被确定了,那么是否按下第二个按钮也就决定了(如果第一个按钮与期望不同,则按下,如果期望相同,则不按下)同理,第三个按钮是否按下也唯一确定。
而不按也能确定一种方法,只有这两种分类;
所以,本题只要分两种情况:按钮1被按下和没有被按下 之后使用for循环判断别的按钮是否需要按下即可
当循环结束,若现在的按钮状况与答案相同,则输出两种方案中按键次数最少的,若不同,则impossible!因为就两种方案,能获得最终结果。

import java.util.Scanner;
public class pro2 {
    //总的 思路就是 前面的序号如果不一样 不要按不一样的那个序号,而是按下一个,1 2 3 如果1不一样就按2,不要考虑第二个和第三个是否不一样的情况
    //因为该序列经过变换 最终要和另一个序列相同,,按下的每个序号都是不分先后到
    //, 这样每个的状态 由下一个决定。分为 不按第一个,和 按第一个
    public static int NoPress(int[]source,int[]dest) {

        int count=0;
            for(int i=1;i<source.length;i++) { 
                if(source[i-1]!=dest[i-1]) {
                //判断前一个 序号是否匹配
                    source[i-1]=1-source[i-1];
                    source[i]=1-source[i];
                    count++;
                    if(i+1==source.length) {
                    //最后一个 被过滤了,,,它的状态由 倒数第二个决定
                        break;
                    }
                    source[i+1]=1-source[i+1];
                }

            }
        if(matching(source,dest)==source.length) {
            return count;
        }
        else return -1;

    }

    public static int Pressed(int[]source,int[]dest) {
    //如果第一个按钮 按下了
        source[0]=1-source[0];
        source[1]=1-source[1];
        int count=1;
        try {
            for(int i=1;i<source.length;i++) {
                if(source[i-1]!=dest[i-1]) {
                    source[i]=1-source[i];
                    source[i-1]=1-source[i-1];
                    count++;
                    if(i+1==source.length) {
                        break;
                    }
                    source[i+1]=1-source[i+1];
                }
            }
        }catch(Exception e) {
            e.printStackTrace();
        }

        if(matching(source,dest)==source.length) {
            return count;
        }
        return -1;
    }

    public static int matching(int []source,int[]dest) {
        int i=0;
        for(;i<source.length;i++) {
            if(source[i]!=dest[i]) {
                break;
            }
        }

        return i;
    }

    public static void main(String []args){
        String source=null;
        String dest=null;

        Scanner in=new Scanner(System.in);
        while(true) {
            System.out.println("请输入原始状态: ");
            source=in.next();
            System.out.println("请输入目标状态: ");
            dest=in.next();
            int []s=new int[source.length()];
            int []d=new int[dest.length()];
            int []s2=new int[source.length()];
            int []d2=new int[dest.length()];
            //为什么要分s和s2呢?因为经过下面的函数后s会发生改变,
            //不再是原来的s,因为java的引入参数都是引用,会改变主调函数
            //里s的值,所以准备两套,分别传进两个函数
            for(int i=0;i<s.length;i++) {
                s[i]=source.charAt(i)-48;
                d[i]=dest.charAt(i)-48;
                s2[i]=source.charAt(i)-48;
                d2[i]=dest.charAt(i)-48;
            }

            int p=Pressed(s,d);

            int np=NoPress(s2,d2);
            //因为不匹配会返回-1,如果两个都不是-1就输出,小的那个
            if(p!=-1&&np!=-1) {
                int min=np>p? p:np;
                System.out.println("最少次数为"+min);
            }else if(p==-1&&np==-1){
                System.out.println("impossible");
            }else {
                int max=np>p? np:p;
                System.out.println("最少次数为"+max);
            }


        }


    }

}
点赞