计蒜客 A2236 马的管辖 暴力枚举 状态压缩

题目描述

原题链接

分析

结果填空题, 不用考虑时间复杂度,直接暴力枚举每一种方案
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;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值