由于本题数据范围不是很大,可以使用暴力枚举每个方案,判断是否符合全部打开状态,之后更新最小答案并记录路径。
1.枚举每个方案
可以使用二进制进行枚举,本题一共16个开关,每种开关有两种选择,打开或者关闭,则一共有2^16次方个方案,因此我们可以从0枚举到1<<16-1,每个数的二进制数代表一种方案。二进制中我们假设1表示进行操作,0表示不操作。
2.将二维数组转换一维数组
由于我们需要判断一个数中前16个二进制位上的数字,可以使用二维数组转换一维数组进行处理。
也就是找到二维坐标对应的一维坐标,然后判断该位上的二进制数。
比如: (2,1) 对应的一维数组的位置就是2*4+1=9 (下标均从0开始)
3.要注意的问题
(1)每次执行完一个方案之后,记得把图形恢复原状,进行下一次判断。
(2)题目中要求字典序输出路径,而我们枚举每个开关状态的时候,就是按照顺序枚举的,保存路径时符合字典序,因此不用额外考虑。
(3) 注意更新答案的条件。
import java.util.*;
public class Main {
// 存放开关
static char[][] g=new char[4][4];
static char[][] temp=new char[4][4];
// 存放路径
static List<int[]> list=new ArrayList<>();
static int res=0;
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
// 读取数据
for(int i=0;i<4;i++) {
String s=sc.next();
for(int j=0;j<4;j++) {
g[i][j]=s.charAt(j);
}
}
// for(int i=0;i<4;i++) {
//
// for(int j=0;j<4;j++) {
//
// System.out.print(g[i][j]);
//
//
// }
// System.out.println();
//
// }
// 使用二进制枚举我们的方案
// 16个开关 每个开关都有开和关两种状态 因此总的方案数是2^16个
//假设 1表示按 0表示不按
for(int k=0;k<(1<<16);k++) {
// 改变状态
// 统计步数
int step=0;
List<int[]> t=new ArrayList<>();
// 拷贝地图
for(int i=0;i<4;i++) {
for(int j=0;j<4;j++) {
temp[i][j]=g[i][j];
}
}
for(int i=0;i<4;i++) {
for(int j=0;j<4;j++) {
// 判断当前方案 对于当前位置的操作
// 若这种方案对于当前位置是点亮 则转换状态
if(((k>>get(i,j))&1)==1) {
// 转换所在的行和列
turn_all(i,j);
step++;
t.add(new int[] {i,j});
}
}
}
// 一种方案执行完毕之后 判断结果是否全部打开
boolean has_closed=false;
for(int i=0;i<4;i++) {
for(int j=0;j<4;j++) {
if(g[i][j]=='+') {
has_closed=true;
}
}
}
// 判断是否需要更新答案
// 若当前path里面没有路径 或者当前花费的步数比之前的小 则更新答案
if(!has_closed) {
if(list.isEmpty()||res>step) {
list=t;
res=step;
}
}
// 恢复地图
for(int i=0;i<4;i++) {
for(int j=0;j<4;j++) {
g[i][j]=temp[i][j];
}
}
}
// 输出答案以及路径
System.out.println(res);
for(int i=0;i<list.size();i++) {
int[] a=list.get(i);
// 由于我们是从0开始的计数 因此需要+1
System.out.println((a[0]+1)+" "+(a[1]+1));
}
}
// 将二维坐标转换为一维坐标
public static int get(int x,int y) {
// 比如坐标为(2,1) 则转换为一维(从0开始)就是9
return x*4+y;
}
// 转换当前点所在的行和列
public static void turn_all(int x,int y) {
for(int i=0;i<4;i++) {
// 转换列
turn_one(x,i);
// 转换行
turn_one(i,y);
}
// 此时x,y这个点被转换了两次 我们进行恢复
turn_one(x,y);
}
// 转换一个点的状态
public static void turn_one(int x,int y) {
if(g[x][y]=='+') {
g[x][y]='-';
}else {
g[x][y]='+';
}
}
}