《炮兵阵地》解题报告

《炮兵阵地》解题报告
Description
司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:

如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

Input
第一行包含两个由空格分割开的正整数,分别表示N和M;
接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。

Output
仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。

Sample Input
5 4
PHPP
PPHH
PPPP
PHPP
PHHP

Sample Output
6

思路:
    因为听说是经典的状态压缩DP才做的,所以即使听闻有人用搜索过了,我还是决定试一下用DP(不过其实是十分类似与搜索的)。
    解题思路大致是这样的,假设地图存在map数组中,对于map[i][j]如果是P,就可以有放兵与不放兵两种方案,但对于在i和j位置之前的所有P的摆放方案有多种方案,我们将这两种方案分别去尝试与Pij之前所有方案结合,则Pij又会有很多种方案。
   
    即,取status[i]={从左上方开始第i个P的方案集} (ij位置上是P),动态方程可以表示为:
    status[i] = status[i-1] ∪ {x∈status[i-1] | valid(x & Pi)}        x & Pi 是指将Pi放在x方案中,valid是检验方案的合法性。
   
    valid函数可以用采用以下约束条件:
        当前方案中,Pi的位置上无士兵或可能被上下攻击的可能。
        当前方案中,Pi的左边2格无士兵,右边2格也无士兵,即无左右受攻击的可能。

    光有这些是不够的,因为无论从空间还是时间上都会超出题目要求的范围。

    为了解决空间问题,我们可以对行进行压缩状态,我采用类似4进制的表示方式来压缩状态:
        encode(int[] solution, int length){
            int result = solution[0]*1 + solution[1]*(4) ... + solution[radix-1]*(4^(length-1))
        }
    decode类似。

    比如 PHPP,对于第三个P就可以有以下几种状态:
        3000、0030、0000、3003、0003

    在进入下一行的动规时,需要对每个非0数减一,依上例,经过一行后得:
        2000、0020、0000、2002、0002

    该状态的某个位置上有数,表示在纵向上有士兵可以攻击,横向的攻击直接求左边两个是否为3就行了。因为DP顺序是自左向右、自上向下的。所以对于每个士兵的攻击只需考虑两个方向而不是四个方向。

    为避免浪费大量空间,根据动态方程,我们只需要保留status[i-1] 的状态集就行了,然后更新status[i]为status[i-1],继续求解。所以我取BitSet记录status[i]的所有状态集(因为状态已经可以被压缩为一个int值了),另外我用索引数组的方式记录某状态当前的最佳值。
    同样以PHPP为例:3000可以压缩为3(反过来压也可以,只要解压和压缩是对称的就行),其solution[3] =1,0030压缩为48,solution[48]=1,3003压缩为195,solution[195]=2。在对以后每行的状态集的维护中,记得保持solution[x]的最大值。
    对于更新status[i]要注意,求status[i]时使用的BitSet是status[i-1]的,千万不要用正在修改的status[i]的BitSet求,可以使用一个BitSet backup来辅助保存。

    通过以上处理,基本上就可以保证空间上不超时了,但是时间的问题依然存在。由于status[i]的BitSet在不断扩大,所以每个更新status[i+1]都是件越来越麻烦的事情。为了控制BitSet的大小,需要像搜索中剪支一样,剪去一些不可能产生最优解的状态。
    我使用的剪支方案是,单行的最大摆放士兵数是(int)((m+2)/3),所以对于某一状态来说,如果该状态的解值+单行最大摆放士兵数<目前最优解,那么就可以忽略该方案了。因为我们的状态记录的是一行的状态情况,所以对于第i行某一状态而言,加上下行最大的摆放士兵数都无法达到最优解的话,那么就没必要继续保留该状态到下行了,所以可以略去。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值