“飞行员兄弟”这个游戏,需要玩家顺利的打开一个拥有 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;
}
}