飞行员兄弟(暴力枚举及详细代码加注释)

飞行员兄弟(暴力枚举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

思路

暴力枚举的方法来做(区别于费解的开关只需要枚举一行的情况),因为每个开关的状态会影响其一整行和一整列的开关,因此一个开关受太多开关控制,无法固定某些开关来做。

在此题中共有4*4个开关,每个开关均只有开和关两种状态,所以总的枚举方案即2^16种。

关键步骤如下:

for(int op=0;op<pow(2,16);op++) 枚举操作数
【0,15】是枚举操作数的位数
if ((op >> get(j, k) & 1) == 1)来判断当前二进制数的当前位数是否为1
get()为获取当前x行y列元素为从左到右从上到下的位数
turn()为翻转整行整列的函数

源代码如下:

//枚举所有的方案

import java.util.*;

import static java.lang.Math.pow;

class Main {

    //存放原数据
    static char[][] bg = new char[10][10];
    //存放当前操作的复制数据
    static char[][] g = new char[10][10];


    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        for (int i = 0; i < 4; i++) {
            String s = scanner.next();
            for (int j = 0; j < 4; j++) {
                bg[i][j] = s.charAt(j);
            }
        }
        work();

    }

    static void work() {
        //用来存放最终结果
        String[] red = new String[20];
        int n = 0;
        //枚举所有方案
        for (int op = 0; op < pow(2, 16); op++) {
            //用来存放当前递推选择下的结果
            String[] res = new String[20];
            int index = 0;
            for (int j = 0; j < 4; j++) {
                for (int k = 0; k < 4; k++) {
                    g[j][k] = bg[j][k];
                }
            }
            //翻转当前递推情况并保存翻转情况
            for (int j = 0; j < 4; j++) {
                for (int k = 0; k < 4; k++) {
                    //op右移get位之后的最后一位是1,那么这个判断语句即成立
                    if ((op >> get(j, k) & 1) == 1) {
                        turn(j, k);
                        res[index++] = (j + 1) + " " + (k + 1);
                    }
                }
            }
            //当前情况是否成功
            boolean success = true;
            for (int i = 0; i < 4; i++) {
                for (int j = 0; j < 4; j++) {
                    if (g[i][j] == '+') {
                        success = false;
                        break;
                    }
                }
            }


            if (success == true) {
                if (n == 0 || n > index) {
                    for (int i = 0; i < index; i++) {
                        red[i] = res[i];
                    }
                    n = index;
                }
            }
        }

        System.out.println(n);
        for (int i = 0; i < n; i++) {
            System.out.println(red[i]);
        }
    }

    static int get(int x, int y) {//获取到当前递推的右移位数
        return x * 4 + y;
    }

    static void turn(int x, int y) {//翻转整行整列
        for (int i = 0; i < 4; i++) {
            turn_xy(x, i);
        }
        for (int i = 0; i < 4; i++) {
            turn_xy(i, y);
        }
        turn_xy(x, y);
    }

    static void turn_xy(int x, int y) {
        if (g[x][y] == '-') {
            g[x][y] = '+';
        } else {
            g[x][y] = '-';
        }
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值