题目
在一个n*n的网格里,每个网格可能称为“墙壁”和“街道”。现在在街道放置碉堡,每个碉堡可以向上下左右四个方向开火。子弹射程无限远,墙壁可以阻挡子弹。问最多能放置多少个碉堡,使他们彼此不会互相摧毁。
算法描述
算法中用二维int数组表示网格,每个网格都有其索引值,对n*n的网格,从左到右,从上到下,索引值依次为0~n*n-1。数组中0表示道路,-1表示墙壁,1表示碉堡,如果出现其他数字,则该数组非法。
算法思路是深度优先+回溯算法,从索引0开始,一直到n*n-1,选择是否插入碉堡。如果在索引index时能够插入碉堡,则碉堡计数quantity+1,index+1并继续调用此函数,直至结束后再在index位置取消插入碉堡同时碉堡计数quantity-1,index+1并继续调用,这个过程就是回溯过程。如果在索引index时不能插入碉堡,则直接index+1继续调用本函数。如果index大于最大值时,表示方格遍历完成,将此时的碉堡数量与最大数量进行比较,如果此时quantity=maxQuantity则记录此时的布局状态在ArrayList<int [][]> result中,如果此时quantity>maxQuantity则更新maxQuantity,删除之前的布局记录,并将此时的布局记录到ArrayList<int [][]> b中。
boolean check(int[][] a)用于安全性检查,检查数组a是否合法。
void show(int[][] a)用于打印a的布局|表示边界,═表示墙,*表示碉堡,空白表示道路
int[][] createBlock(int size, int... wall_location)用于创建一个布局,size表示布局的边长,后面的int参数用于表示墙壁的索引值,如果只有第一个参数则表示创建没有墙壁的布局。函数返回该布局的数组。
boolean setFort(int[][] a, int fort_location)用于在布局a中指定位置放置碉堡,如果放置成功,返回true,防止失败返回false。
void fillFort(int[][] a, int index)为上文提到的深度优先+回溯算法的函数,具体思想上文有详细描述。
代码实现
import java.util.ArrayList;
public class Problem3 {
//0表示道路,-1表示墙,1表示碉堡
private static int maxQuantity = 0;//用于记录最多的碉堡数量
private static int quantity = 0;//用于记录已安插碉堡的数量
private static ArrayList<int[][]> result = new ArrayList<>();//用于存储最优解
private static boolean check(int[][] a) {
//检查网格是否合法,即是否为n*n,是否仅有-1,0,1
for (int[] ints : a) {//对传入的数组进行参数合法性检查
if (ints.length != a.length) {//数组应为n*n
return false;
} else {//数组内不能出现不是-1,0,1的数字
for (int x : ints) {
if (x != 0 && x != -1 && x != 1) {
return false;
}
}
}
}
return true;
}
private static void show(int[][] a) {
//打印指定网格内的墙壁,堡垒道路情况,|表示边界,═表示墙,*表示碉堡,空白表示道路
if (!check(a)) {
throw new IllegalArgumentException("传入参数不合法");
}
//对a进行合法校验,是否满足网格要求
System.out.print(" ");
for (int i = 0; i < a.length; i++) {
System.out.print(" _ ");
}
System.out.println("");
for (int[] x : a) {
System.out.print("|");
for (int i : x) {
switch (i) {
case -1:
System.out.print(" ═ ");
break;
case 0:
System.out.print(" ");
break;
case 1:
System.out.print(" * ");
}
}
System.out.println("|");
}
System.out.print(" ");
for (int i = 0; i < a.length; i++) {
System.out.print(" ˉ ");
}
System.out.println("");
}
private static int[][] createBlock(int size, int... wall_location) {
//创建一个网格和其中的墙,size为网格大小,wall_location表示墙的位置
if (size <= 0) {
throw new IllegalArgumentException("请传入大于零的网格尺寸");
}
//进行参数合法性校验
int[][] a = new int[size][size];
for (int i : wall_location) {
if (i < 0 || i >= size * size) {
throw new IllegalArgumentException("墙壁位置越界");
} else {
a[i / size][i % size] = -1;
}
}
return a;
}
private static int[][] createBlock(int size) {
//创建一个无墙网格,size为网格大小,无wall_location表示没有墙
if (size <= 0) {
throw new IllegalArgumentException("请传入大于零的网格尺寸");
}
//进行参数合法性校验
int[][] a = new int[size][size];
return a;
}
private static boolean setFort(int[][] a, int fort_location) {
if (!check(a)) {
throw new IllegalArgumentException("网格不规范");
} else if (fort_location < 0 || fort_location >= a.length * a.length) {
throw new IllegalArgumentException("碉堡位置越界");
}
int x = fort_location / a.length;
int y = fort_location % a.length;//插入碉堡的横纵坐标
if (a[x][y] != 0) {
return false;
}//如果当前位置不是空白,则不能插入,返回false;
//如果是空白,判断其上下左右
for (int i = x - 1; i >= 0; i--) {//判断其上方
if (a[i][y] == -1) {
break;
} else if (a[i][y] == 1) {
return false;
}
}
for (int i = x + 1; i < a.length; i++) {//判断其下方
if (a[i][y] == -1) {
break;
} else if (a[i][y] == 1) {
return false;
}
}
for (int j = y - 1; j >= 0; j--) {//判断其左方
if (a[x][j] == -1) {
break;
} else if (a[x][j] == 1) {
return false;
}
}
for (int j = y + 1; j < a.length; j++) {//判断其右方
if (a[x][j] == -1) {
break;
} else if (a[x][j] == 1) {
return false;
}
}
//全都没问题则添加碉堡并返回true
a[x][y] = 1;
return true;
}
private static void fillFort(int[][] a, int index) {
if (!check(a)) {
throw new IllegalArgumentException("请传入合法的网格");
} else if (index < 0 || index > a.length * a.length) {
throw new IllegalArgumentException("请传入合法的index");
}
if (index == a.length * a.length) {
if (quantity >= maxQuantity) {
int[][] m = new int[a.length][a.length];
for (int i = 0; i < a.length; i++) {
for (int j = 0; j < a.length; j++) {
m[i][j] = a[i][j];
}
}
if (quantity > maxQuantity) {
maxQuantity = quantity;
result.clear();
}
result.add(m);
}
} else {
if (setFort(a, index)) {
quantity++;
fillFort(a, index + 1);
a[index / a.length][index % a.length] = 0;
quantity--;
}
fillFort(a, index + 1);
}
}
public static void main(String[] args) {
int[][] a = createBlock(4, 1, 8, 9);
//创建网格和墙壁,该参数表示4*4的网格中,1,8,9位置为墙
System.out.println("地形如下:");
show(a);//打印未安插碉堡的地图
fillFort(a, 0);
//对创建的网格进行填充,得到的最多碉堡数量记录在maxQuantity中,填充方式记录在result中
System.out.println("最多安插" + maxQuantity + "个碉堡,共找到" + result.size() + "组最优解,它们分别是:");
for (int i = 0; i < result.size(); i++) {
show(result.get(i));
}
}
}