题目传送
在中国象棋中,马是走日字的。一个马的管辖范围指的是当前位置以及一步之内能走到的位置,下图的绿色旗子表示马能走到的位置。
如果一匹马的某个方向被蹩马脚,它就不能往这个方向跳了,如下图所示,海星的位置存在旗子,马就不能往上跳到那两个位置了:
那么问题来了,在一个 n×m 的棋盘内,如何用最少的马管辖住所有 n×m 个格子。比如 n=m=3 时,最少要用 5 只马才能管辖所有棋盘,一种可能的方案如下:
当 n=m=5 时,请你求出用最少马管辖的 方案个数。
分析:
其实一开始我以为对于3*3的棋盘,两匹马不就够了吗,两匹马的情况下就能覆盖全部的位置;但是题目给的是5匹马;那么题意应是在马跳一次的前提下(以马当前所在的位置,朝8个方向跳一次),能够覆盖全盘的最少马管辖的 方案个数
那么暴力一下就更好了,一般来说10s只能是可以出答案的,还有一个地方就是这个马好像走的是“目”……
说一下怎么暴力吧,说是用到了二进制,怎么个用法呢
我们来看一下下面的数值:
(
1
)
10
=
(
0001
)
2
(1)_{10}=(0001)_2
(1)10=(0001)2
(
2
)
10
=
(
0010
)
2
(2)_{10}=(0010)_2
(2)10=(0010)2
(
3
)
10
=
(
0011
)
2
(3)_{10}=(0011)_2
(3)10=(0011)2
(
4
)
10
=
(
0100
)
2
(4)_{10}=(0100)_2
(4)10=(0100)2
…
…
……
……
(
15
)
10
=
(
1111
)
2
(15)_{10}=(1111)_2
(15)10=(1111)2
在10进制转二进制你会发现我们只需要用到15,就能将4个位置的放置情况枚举出来(1代表有,0代表没有),那么题目中是5*5的方格,那么我们该怎么去枚举情况呢;
其实很简单,我们把这个5*5个方格,也就是25个方格想象成25个位置不就好了,这样我们只需要暴力枚举225的放置情况就好了
具体情况看代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#include <bitset>
#include <set>
#include <utility>
#include <sstream>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define inf 0x3f3f3f3f
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define lep(i,l,r) for(int i=l;i>=r;i--)
#define ms(arr) memset(arr,0,sizeof(arr))
//priority_queue<int,vector<int> ,greater<int> >q;
const int N = 1e5 + 10;
const int mod = 1e9+7;
int a[30];
int g[10][10];
int minv=inf;
int dx[]={-2,-2,-1,1,2,2,1,-1};//马跳的情况
int dy[]={-1,1,2,2,1,-1,-2,-2};
int pdx[]={-1,-1,0,0,1,1,0,0};//马蹩腿的方向
int pdy[]={0,0,1,1,0,0,-1,-1};
int Arrange(int X){
int cnt=0;
for(int i=1;i<=5;i++){
for(int j=1;j<=5;j++){
if(X%2)//利用二进制的特点安排马的放置情况
g[i][j]=1;
else
g[i][j]=0;
cnt+=g[i][j];
X/=2;
}
}
if(cnt>minv)//算是一个剪枝吧
return 0;
for(int i=1;i<=5;i++){
for(int j=1;j<=5;j++){
if(g[i][j]==1){//当前位置有放置的马
for(int k=0;k<8;k++){
int x=i+dx[k],y=j+dy[k];
int tx=i+pdx[k],ty=j+pdy[k];
if(x>5 || x<1 || y>5 || y<1) continue;
if(tx>5 || tx<1 || ty>5 || ty<1) continue;
if(g[tx][ty]==1) continue;//已放置马了就不需要标记了
if(g[x][y]==0) g[x][y]=2;//标记一下,说明有一匹马可以通过走日到达,标记符号可以用1之外的数字,这里我用的是2
}
}
}
}
for(int i=1;i<=5;i++){
for(int j=1;j<=5;j++){
if(g[i][j]==0){
return 0;
}
}
}
minv=cnt;
return cnt;
}
int main()
{
int ub=(1<<25);
for(int i=1;i<ub;i++){
int t=Arrange(i);//枚举所有的情况
if(t)
a[t]++;
}
for(int i=1;i<=25;i++)
if(a[i])
cout<<a[i]<<endl;
return 0;
}