AcWing 116. 飞行员兄弟(二维指数型枚举)

116. 飞行员兄弟

思路:

  • 通过二进制串暴力枚举所有操作情况
  • 从左上到右下扫描二进制串 (当一个答案存在多种操作情况时保证优先级整体从上到下,同行从左到右打开)
  • 把每次的操作坐标存放到ArrayList<int[]> temp
  • 特判一下每次操作结束后的结果是否符合题意
  • 如果符合题意则用temp先初始化size=0的ans,如果后面存在比ans的size更小的操作情况则更新ans
  • 时间复杂度: 2 16 ∗ ( 16 ∗ 7 + 16 ) 2^{16}*(16*7+16) 216(167+16)
import java.util.*;
import java.io.*;

public class Main {
    Scanner s = new Scanner(System.in);
    BufferedWriter log = new BufferedWriter(new OutputStreamWriter(System.out));

    // 记录输入的矩阵
    char[][] g;

    // 记录每次操作的坐标
    ArrayList<int[]> list;
    
    // 记录输入的矩阵每一行
    String line;

    public void run() throws Exception {
        list = new ArrayList<>();
        g = new char[4][4];
        // 输入矩阵
        for (int i = 0; i < 4; i++) {
            line = s.next();
            for (int j = 0; j < 4; j++) {
                g[i][j] = line.charAt(j);
            }
        }

        // 打印最小步数与每一步的操作坐标
        minStep(g);
        log.flush();
    }

    public void minStep(char[][] c) throws Exception {
        char[][] backup = new char[4][4];

        for (int i = 0; i < 4; i++)
            System.arraycopy(c[i], 0, backup[i], 0, 4);
        
        // 保存最终的答案对应的操作坐标
        ArrayList<int[]> ans = new ArrayList<>();

        // 枚举二维二进制串
        for (int op = 0; op < 1 << 16; op++) {

            // 扫描二维二进制串,对二进制串中标记为1的数字对应的位置进行操作,并记录一下操作的坐标
            for (int i = 0; i < 4; i++)
                for (int j = 0; j < 4; j++) {
                    if ((op >> get(i, j) & 1) == 1) {
                        turn(backup, i, j);
                        list.add(new int[]{i + 1, j + 1});
                    }
                }

            boolean openAll = true;

            // 扫描一下操作后的矩阵,判断其是否已经全打开
            for (int i = 0; i < 4; i++) {
                for (int j = 0; j < 4; j++) {
                    if (backup[i][j] == '+') {
                        openAll = false;
                        break;
                    }
                }
            }
            
            // 如果已经全打开
            if (openAll) {
                // 判断一下操作次数,更新ans
                if (ans.isEmpty() || ans.size() > list.size()) listCopy(ans, list);
            }

            // 复原
            for (int i = 0; i < 4; i++)
                System.arraycopy(c[i], 0, backup[i], 0, 4);
            list.clear();
        }

        log.write(ans.size() + "\n");
        
        for (int i = 0; i < ans.size(); i++) {
            log.write(ans.get(i)[0] + " " + ans.get(i)[1] + "\n");
        }
    }

    private void listCopy(ArrayList<int[]> desc, ArrayList<int[]> src) {
        desc.clear();
        desc.addAll(src);
    }

    // 二维二进制串中(x, y)坐标对应的二进制串中的索引
    public int get(int x, int y) {
        return x * 4 + y;
    }

    public void turn(char[][] c, int x, int y) {
        // 竖着扫描
        for (int i = 0; i < 4; i++) {
            c[i][y] = c[i][y] == '+' ? '-' : '+';
        }

        // 横着扫描
        for (int j = 0; j < 4; j++) {
            c[x][j] = c[x][j] == '+' ? '-' : '+';
        }

        c[x][y] = c[x][y] == '+' ? '-' : '+';
    }

    public static void main(String[] args) throws Exception {
        new Main().run();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值