飞行员兄弟Java版


“飞行员兄弟”这个游戏,需要玩家顺利的打开一个拥有16个把手的冰箱。 已知每个把手可以处于以下两种状态之一:打开或关闭。只有当所有把手都打开时,冰箱才会打开。把手可以表示为一个4х4的矩阵,您可以改变任何一个位置[i,j]上把手的状态。但是,这也会使得第i行和第j列上的所有把手的状态也随着改变。请你求出打开冰箱所需的切换把手的次数最小值是多少。
输入格式

输入一共包含四行,每行包含四个把手的初始状态。

符号“+”表示把手处于闭合状态,而符号“-”表示把手处于打开状态。

至少一个手柄的初始状态是关闭的。
输出格式

第一行输出一个整数N,表示所需的最小切换把手次数。

接下来N行描述切换顺序,每行输入两个整数,代表被切换状态的把手的行号和列号,数字之间用空格隔开。

注意:如果存在多种打开冰箱的方式,则按照优先级整体从上到下,同行从左到右打开。
数据范围

1≤i,j≤4

输入样例:

-+--
----
----
-+--

输出样例:

6
1 1
1 3
1 4
4 1
4 3
4 4

思路:

这题相当于费解开关的另一个版本,那题是我第一次写题解,有兴趣的可以看一哈,这一题是我第二次写哈哈哈 ,只是这个数据很小能暴力出来,而且这个每改变一个手柄的状态,也会使得第i行和第j列上的所有把手的状态也随着改变,所以就不用管切换把手的顺序和方向了,直接暴力,每个把手收就改变和不改变两种做法,总的次数2的16次方65 536 也不是很多,

具体做法:
每种可能性用16位的二进制表示 每一位的1表示要改变,0表示不改变
首先看成16个格子,0~15这么排开

0    1    2    3 
4    5    6    7 
8    9   10    11
12   13  14    15

格子的中表示的数分别对应16二进制中的第i个数,看它是0还是1,1表示要改变,0表示不改变

`for (int i = 0; i < 1 << 16; i++) {
            //这题数据比较小 把所有的情况全部写出来 2的十六次方
     for (int m = 0; m < 4; m++) {
          for (int n = 0; n < 4; n++) {
                if ((i >> (m * 4 + n) & 1) == 1)   //不难发现每行的行列与对应的数字的关系 位运算是1就改变
                
                
                  // 如果对应位数是1就改变其状态
                  turn(m,n);
                    }
                    }
                    }

举个例子 第一次i=0000 0000 0000 0000

0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0

第一种情况是全部都不按 ,类似的接下来的几种情况我们也都可以推导出来,总有题目要求有解则说明总有几种情况是可以的
由于我们是从第0位开始,所以我们找到的解法肯定是字典序最小的
还有就是每次判断一个方案是否可行,map数组都会改变,为了答案的准确性我们应该在每次循环后还原数组
然后上代码

import java.util.Scanner;

public class Main {
    public static char map[][] = new char[5][5];
    public static char maptemp[][] = new char[5][5];//用于还原数组
    public static int res[][] = new int[100][3];
    static int indexx = 0;
    static int ans = 1000;

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        for (int i = 0; i < 4; i++) {
            String temp = input.next();
            for (int j = 0; j < 4; j++) {
                map[i][j] = temp.charAt(j);
                maptemp[i][j] = map[i][j];
            }
        }
        dfs(map, maptemp, 0);


    }

    public static void dfs(char map[][], char temp[][], int step) {
        int tempx = 0;//用于记录anss数组的长度
        int anss[][] = new int[1000][3];//z最后输出的记录每一步的数组
        for (int i = 0; i < 1 << 16; i++) {
        //这题数据比较小 把所有的情况全部写出来 2的十六次方
            for (int m = 0; m < 4; m++) {
                for (int n = 0; n < 4; n++) {
                    if ((i >> (m * 4 + n) & 1) == 1) {
                        turn(m, n);
                        step++;
						//找一个数组储存走的每一步
                        res[indexx][0] = m;
                        res[indexx++][1] = n;
                    }
                }
            }
            int flag = 0;
            //校验把手是否全部打开
            for (int m = 0; m < 4; m++) {
                for (int n = 0; n < 4; n++) {
                    if (map[m][n] == '+') {
                        flag = 1;
                        break;
                    }
                }
            }
            //如果打开则更新步数 和坐标数组
            if (flag == 0) {
                if (ans > step) {
                    tempx = indexx;
                    ans = step;
                    for (int j = 0; j < indexx; j++) {
                        anss[j][0] = res[j][0];
                        anss[j][1] = res[j][1];
                    }
                }

                }

                //还原数组,还原步数,
                for (int k = 0; k < 5; k++) {
                    for (int m = 0; m < 5; m++) {
                        map[k][m] = temp[k][m];
                    }
                }

                step = 0;
                indexx = 0;//还原数组长度
            }
            //输出结果
            System.out.println(ans);
            for (int i = 0; i < tempx; i++) {

                System.out.println((anss[i][0] + 1) + " " + (anss[i][1] + 1));
            }


        }
        public static void turn ( int x, int y){
            for (int i = 0; i < 4; i++) {
                //x轴上的变
                if (map[x][i] == '-') {
                    map[x][i] = '+';
                } else
                    map[x][i] = '-';
                //y轴上的变
                if (map[i][y] == '-') {
                    map[i][y] = '+';
                } else
                    map[i][y] = '-';


            }
            //由于X轴 Y轴变了两次 x y 等于没变,最后还要变一下
            if (map[x][y] == '-')
                map[x][y] = '+';
            else
                map[x][y] = '-';
        }

    }

~~才开始写题解没有经验,有点乱,希望大家,下手轻点
对了这题是我看y总的视频后才写出来的,y总nb