马的管辖
在中国象棋中,马是走日字的。一个马的管辖范围指的是当前位置以及一步之内能走到的位置,下图的绿色旗子表示马能走到的位置。
如果一匹马的某个方向被蹩马脚,它就不能往这个方向跳了,如下图所示,海星的位置存在旗子,马就不能往上跳到那两个位置了:
那么问题来了,在一个 n×m 的棋盘内,如何用最少的马管辖住所有 n×m个格子。比如 n=m=3 时,最少要用 5 只马才能管辖所有棋盘,一种可能的方案如下:
思路
状态压缩,把二维的棋盘压缩到一维的数组中(n行m列存入一维大小为n*m的数组中),然后二进制求出所有的情况。
然后对于每种情况转化为二维坐标,判断有马的地方要跳的位置(以当前点为源点,可以循环出所有可能的方向)。
对于可以跘马脚的位置,由坐标的关系可以得出然后再判断可不可以跳。
用sum变量记录总的覆盖数(刚开始为马的数量),判断是否覆盖完来决定是否可取。
最后注意要求的是方案数,然后对于每个满足条件的最少马数来求答案。
import java.util.Scanner;
public class Main {
static int m,n;
public static void main(String[] args){
Scanner cin=new Scanner(System.in);
n = 5;
m = 5;
int ans = n*m; //最少马数
int cnt = 0; //对应方案数
for(int i=0; i<(1<<(n*m)); i++) { //枚举每个格子的01状态
int[][] map = new int[n+5][m+5];
int sum = 0;
for(int j=0; j<(n*m); j++) {
int t = ((1<<j)&i) > 0 ? 1 : 0;
sum += t;
int row = (j+m)/m; //转化为二维坐标
int col = (j+1)%m;
if(col == 0) col = m;
map[row][col] = t;
}
if(getAns(map, sum)) {
if(ans == sum){
cnt ++;
}else if(ans > sum){
cnt = 1;
ans = sum;
}
}
}
System.out.println(cnt);
}
static int[][] dir = {{-2,-1}, {-2,1}, {-1,-2}, {-1, 2},
{1, -2}, {1, 2}, {2, -1}, {2, 1}};
private static boolean getAns(int[][] map,int sum) {
//1 马
//2 覆盖
for(int i=1; i<=n; i++) {
for(int j=1; j<=m; j++) {
if(map[i][j] == 1) {
for(int d=0; d<8; d++) {
int x = i + dir[d][0]; //能到的八个方向
int y = j + dir[d][1];
if(x < 1 || x > n || y < 1 || y > m) continue;
if(map[x][y] == 0) { //不考虑别马脚的情况可以到的情况
int xi = dir[d][0] / 2 + i; //别马脚所处的位置
int yj = dir[d][1] / 2 + j;
if(map[xi][yj] == 1) continue; //该位置有马
map[x][y] = 2;
sum ++;
}
}
}
}
}
return sum == n*m ? true : false;
}
}