题目描述
分析
结果填空题, 不用考虑时间复杂度,直接暴力枚举每一种方案
5 × 5 5×5 5×5的棋盘, 每一个格子有放或不放马两种状态, 所以一共需要枚举 2 25 2^{25} 225种方案
每一种方案的具体放法, 压缩在一个数中(通过遍历数的二进制的前 25 25 25位得到具体的放法)
检查每种方案是否合法(遍历所有的马, 判断马是否管辖了所有的格子,注意别马腿的情况),
从而得到方案数
此题状态压缩的做法, 和AcWing 95. 费解的开关类似
答案
最少用 9 9 9匹马, 共 90 90 90种方案
实现
// 方案数: 最少用9匹马, 共90种方案
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int map[5][5]; // 1表示该格子有马, 或者被管辖
int dx[] = {0,1,0,-1}, dy[] = {1,0,-1,0}; // 用于检查马的四周是否有马,被别马腿
int spx[4][2] = {{-1,1},{2,2},{1,-1},{-2,-2}}, spy[4][2] = {{2,2},{1,-1},{-2,-2},{-1,1}}; // 马所能跳到的位置
bool vis[5][5];
int ans; // 方案总数
int min_cnt = 0x3f3f3f3f; // 合法方案中, 马最少的数量
int cnt; // 当前方案中, 马的数量
bool check() // 检查当前方案是否合法 (若棋盘所有格子有马,或者被管辖,则合法)
{
for(int i=0; i<5; i++)
for(int j=0; j<5; j++)
if(!vis[i][j]) return false;
return true;
}
bool in(int x, int y) // 判断坐标是否合法
{
if(x < 0 || x >= 5 || y < 0 || y >= 5) return false;
return true;
}
int main()
{
for(int i=0; i<1<<25; i++) // 5x5的方格, 每个格子有两种状态, 所以一共2^25种状态, 并把状态压缩到i中;
{
cnt = 0;
memset(map, 0, sizeof(map));
memset(vis, 0, sizeof(vis));
for(int j=0; j<25; j++) // 将状态压缩到i中, 取i的25位二进制数, 来表示5×5的棋盘的某一方格,是否有马
{
if(i>>j&1)
{
map[j/5][j%5] = 1;
vis[j/5][j%5] = 1;
cnt++;
}
}
if(cnt > min_cnt) continue; // 当前方案放的马多, 那就没必要检查是否合法
for(int j=0; j<5; j++)
{
for(int k=0; k<5; k++)
{
if(!map[j][k]) continue; // 如果当前格子有马, 就标记该马所能管辖的格子(注意别马腿的情况)
for(int l=0; l<4; l++)
{
int nx = j + dx[l], ny = k + dy[l];
if(!in(nx, ny) || map[nx][ny]) continue; // 如果没被别马腿, 就标记该马所能管辖的格子
{
for(int p=0; p<2; p++)
{
int nx = j + spx[l][p], ny = k + spy[l][p];
if(in(nx, ny)) vis[nx][ny] = 1; // 标记
}
}
}
}
}
if(check()) // 当前方案合法
{
if(cnt < min_cnt) // 如果当前方案所用马少, 那就重新计数
{
min_cnt = cnt;
ans = 0;
}
ans++;
}
}
cout << ans << endl;
return 0;
}