AcWing.飞行员兄弟(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矩阵,每次动一个开关会改变所在行所在列的开关
因此这个题是会把所有行所有列给影响,不能再像 翻硬币和费解的开关一样
因此可以直接枚举所有的可能性
4*4个开关,每个开关有动与不动两种方案,一共2^16个可能
可以把2^16优化成 2 << 16,从二进制上判断  传入一个[i,j]
--> 通过4*i + j 转化成0~15的数
比如:    0000
         1100
         1010
         1111
            转化成:
                    0000110010101111
    这里的1与0不是对应着开关的开关,二十对应着是否要操作这个位置

if((op >> get(i,j) & 1) == 1)   通过循环,右移,得到每一位 与 1 按位与
通过上述操作便可以得到所有的可能性,不断寻找最小的那个值

注意:操作时,每次都会对数据进行修改,操作前记得要将数组备份
 */

import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class Main {
    static final int N = 4;
    static char[][] g = new char[N][N];
    static char[][] backup = new char[N][N];

    //用于村粗每次的操作
    static List<PII> res = new ArrayList<>();

    //输入规模较大,用BufferedReader
    static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

    public static void main(String[] args) throws IOException {
        //拿到输入数据
        for (int i = 0; i < N; i++) {
            g[i] = in.readLine().toCharArray();
        }

        //枚举所有操作可能性
        //'*'的优先级 > '<<' > '<' 所有不需要括号
        for(int op = 0;op < 1 << N * N;op ++){
            //用来存储每次操作的位置下标
            //不断与res比较,得到最小值
            List<PII> temp = new ArrayList<>();

            //操作前要先对数据备份
            for (int i = 0; i < N; i++) {
                backup[i] = g[i].clone();
            }

            //开始判断哪些位置需要操作,调用get方法将坐标转成数字
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) {
                    // == 的优先级大于 &
                    if((op >> get(i,j) & 1) == 1)
                    {
                        //需要操作的化就对temp加入一次
                        temp.add(new PII(i,j));
                        turn(i,j);
                    }
                }
            }
            
            //操作完判断所有位置是否都是 -
            boolean has_closed = false;
            for (int i = 0; i < N; i++) {
                for (int j = 0; j <N; j++) {
                    if (g[i][j] == '+')
                        has_closed = true;
                }
            }
            
            //如果has_closed一直没被改变,就可以看看是否需要更新res
            if (!has_closed){
                if (res.isEmpty() || res.size() > temp.size())
                    res = temp;
            }
            
            //一次操作结束后,记得把数组g还原
            for (int i = 0; i < N; i++) {
                g[i] = backup[i].clone();
            }
        }
        //2^16操作完后,res里存着的就是答案
        System.out.println(res.size());
        for (PII re : res) {
            //res里面存的是下标,还需要+1
            System.out.println(re.x + 1 + " " + (re.y + 1));
        }
        in.close();

    }

    //将下标转成数字,与2^16按位与确定哪些位置需要操作
    public static int get(int x,int y){
        return 4 * x + y;
    }

    //输入坐标x,y,更改x行,x列
    public static void turn(int x,int y){
        for (int i = 0; i < N; i++) {
            if(g[x][i] == '+') g[x][i] = '-';
            else g[x][i] = '+';

            if(g[i][y] == '+') g[i][y] = '-';
            else g[i][y] = '+';
        }
        //多加了一个要再按一下
        if(g[x][y] == '+') g[x][y] = '-';
        else g[x][y] = '+';
    }
}

class PII{
    int x,y;

    public PII(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值