飞行员兄弟(暴力枚举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] = '-';
}
}
}