简易炸弹超人 题解----LQB2023中级组选拔赛

一、题目描述

有一块矩形游戏场地,场地被分成 N* M 的网格(4>N<100,4>M<10)其中一部分小方格水域,另一部分小方格是陆地。
为防御敌军攻击,玩家需要在游戏场地安置炸弹:
1.炸弹只能安置在陆地上;
2.每颗炸弹爆炸后,可以波及到炸弹所在的小方格,及相邻的上、下、左、右小方格;
3.任意两颗炸弹爆炸后不能波及到同一个小方格
请帮助玩家计算出如何安置炸弹,可以使炸弹波及到的范围最大,输出最多可以波及到的小方格数量。
例如:N=4,M=4,网格中水域和陆地的情况如图1所示:

图中,蓝色区域代表水域,绿色区域代表陆地:安置炸弹的最优方案之一如图2所示;炸弹波及的范围如图3所示(黑色区域)这块 4x4 的矩形游戏场地最多可以波及到 11个小方格,其他方案都不会优于这个结果。

输入

第一行输入两个正整数 N和M (4<N<100,4<M<10),分别表示网格的行数和列数,两个正整数之间以一个空格隔开。接下来输入N行,每行 M 个字符 (字符只能是大写字母 A或 B),A 表示水域,B表示陆地,字符之间以一个空格隔开

输出

输出一个整数,表示最多可以波及到的小方格数量

样例输入 

44
BAAA
ABAB
BABB
ABAA

样例输出 

11

二、分析一波

根据题目我们可以得出以下此图:

 备注:图中红色的为炸弹,黄色为波及范围,橙色是不能放置下一个炸弹的位置

当时在测试的我,觉得直接用DFS做即可,但没把代码打完。

逻辑和实现

我们用二进制来标记该位置是否为水域*(0是陆地,1是水)
是否有炸弹也用二进制(1表示安装过了,0表示没有)
SO,这里用俩数组记录该位置,G[i]用来记录该位置是否为水域,S[i]用来记录该位置是否安装过炸弹。
SO这里又要使用“与”运算(&)和“移位”(<<和>>)and 二进制中的常规运算
这里就不讲了,自己可以查:基础的二进制运算(我是小白,也不太会)

回归正题:
当G[i]&s[i]=0时就符合题意
如何判断某一行的状态是否合法,状态中不能有相邻1的间隔小于2:记某一行的状态的为s,只要保证每一个1右边第一一个位置和第二个位置不为1,即可,即s&s>> 1和s&s>> 2均为0。

状态定义

状态表示:
f[i][j][k]:第i行炸弹安装状态为j,且第i-1行的状态为k,能波及到的最大格子数
状态转移:
若第i-2层的状态是u,那么结果就是f[i- 1][k][u] + count(i,c),其中count(i,c)表示第i行的状态为c时可以波及到的小方格数量。
SO,状态转移方程就是: f[i][j][k] = max{f[i][j][k], f[i-1][k][u]+ count(i,c)}。
优化原因:如果不优化,每行最多有2的M次方个状态,会超时,AND空间复杂O为(N*2的M次方*2的M次方),会爆,SO只能搞滚动数组。

接着,都分析到这了,上代码~~~~~~

#include <iostream>
#include <vector>
using namespace std;
const int N=110,M=1 << 10;
int n,m,g[N],cnt[M],f[2][M][M];
vector<int> s, h[M];
bool check(int s){
    return !(s&s>>1||s&s>>2);
}
bool check2(int s){
    return s&s>>1;
}
int count(int x,int s){
    int cnt=0,flag=0;
    for(int i=0;i<m;i++){
        if(s>>i&1){
            flag=1;
            if(x-1>=1) cnt++;
            if (x+1<=n) cnt++;
            if(i-1>=0) cnt++;
            if (i+1<m) cnt++;
        }
        return cnt + flag;
    }

}
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=0;j<m;j++){
            char x;
            cin>>x;
            if(x=='A') g[i]+= 1 <<j;
            }
        }


    for(int i=0;i<1<<m;1++){
        if (check(i)) s. push_ back(i); //筛取在行.上合法的状态
    }

    for(int i=1;i<=n+2;1++){
        for (int j = 0; j < s.size(); j++){ //第i行
            for (int k = 0; k < s.size(): k++) {//第i-1行
                for (int u = 0; u < s.size(); u++) {//第i-2行
                    int a = s[ul. b = s[k]. c= s[i]:
                if((a&b)11(b&c)11(a&c))continue;
                if ((g[i] & c) 11 (g[i - 1] & b)) continue;
                if (check2(a | b)) continue;
                intcnt=i<=n?count(1,c):0;
                f[i & 1][j][k] = max(f[i & 1][j][k], f[i - 1& 1][k][u] + cnt);
                }
            }
        }
    }
    cout << f[n + 2 & 1][0][0] << endl;
    return 0
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值