马的管辖 计蒜客 - A2223

题目传送
在中国象棋中,马是走日字的。一个马的管辖范围指的是当前位置以及一步之内能走到的位置,下图的绿色旗子表示马能走到的位置。
在这里插入图片描述
如果一匹马的某个方向被蹩马脚,它就不能往这个方向跳了,如下图所示,海星的位置存在旗子,马就不能往上跳到那两个位置了:
在这里插入图片描述

那么问题来了,在一个 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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值