炮兵阵地图文详解NOI2001/POJ1185(状态压缩)

本文介绍了一个基于动态规划的算法,用于解决在特定地形条件下,如何最大化部署炮兵部队数量的问题。算法通过预处理地形和炮兵攻击范围,利用二进制状态压缩技巧,实现了高效的求解。

题目描述
司令部的将军们打算在N * M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
在这里插入图片描述
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。 现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

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

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

输入样例:
5 4
PHPP
PPHH
PPPP
PHPP
PHHP
输出样例:
6

算法思路:
解法一:
因为每个位置能否放置炮兵与它上面两行对应位置上是否放置炮兵有关,所以在向第i行的状态转移时,需要知道第i-1行和第i-2行的状态。我们把每一行的状态看作一个M位二进制数,用一个0~2M-1之间的十进制整数存储,其中第p(0≤p<M)位为1表示该行第p列放置了炮兵,为0则表示没有放置炮兵。

我们在DP前预处理出集合S,存储“相邻两个1的距离不小于3”的所有M位二进制数,这些二进制数代表每一行中两个炮兵的距离不能小于3。

设count(x)表示M位二进制数x中1的个数。

设valid(i, x)表示M位二进制数x属于集合S,并且x中的每个1对应在地图第i行中的位置都是平原。

设F[i,j,k]表示第i行压缩后的状态为j,第i-1行压缩后的状态为k时,前i行最多能摆放多少个炮兵。
在这里插入图片描述
F[i,j,k]表示第i行状态为j,第i-1行状态为k ,那么j&k=0表示这两行的摆放炮兵不冲突,那么k的状态不同,对应的j的状态就不同,所以的枚举j和k。
上一行i-1的状态为k,那么就枚举i-1行上一行的状态l,保证l和k不冲突,再保证l和j不冲突,这样就保证了第i行j和第i-1行k,和第i-2行l都不冲突。

理解了上述,代码就好写了,对于第i行枚举j、枚举k、枚举l三层循环,当然j,k,l属于集合S,并且x中的每个1对应在地图第i行中的位置都是平原。
并且 j&k=0且j&l=0

j&l=0表示这两个整数对应的列没有两个都是1,只有两个都是1按位与才是1.

初值:F[0,0,0]=0,其余为负无穷。
目标:
在这里插入图片描述
完整代码:

#include<iostream>
#include<cstdio>
using namespace std;
int n,m,k;
int s[1005],g[1005];
int f[102][1005][1005],ans;
char ma[103];
int map[103];
int get(int x)//计算某一状态含有多少个1(即有多少个炮兵) 
{
   
   
	int e=0;
	while(x>0)
	{
   
   
		++e;
		x-=x&(-x);
	}
	return e;
}
int main()
{
   
   
	cin>>n>>m;
	for(int i=1;i<=n;++i)
	/*读入地图,将山地(不能放兵)的地方设为1 ,同时把一行地形
	转化为一个m为二进制数所对应的整数。*/
	{
   
   
		scanf("%s",ma);
		for(int j=0;j<m;++j)
		  if(ma[j]=='H') map[i]+=1<<j;
	}
	for(int i=0;i<=(1<<m)-1;++i)
	//枚举所有的状态0-2^m-1,处理出满足条件的集合s 
	 if(((i&(i<<1))==0)&&
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值