| Time Limit: 2000MS | Memory Limit: 65536K | |
| Total Submissions: 13278 | Accepted: 4868 |
Description

如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
Input
接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。
Output
Sample Input
5 4 PHPP PPHH PPPP PHPP PHHP
Sample Output
6
这个题目是一道典型的状态压缩DP 。此题的位运算用到比较多。
状压DP 可以说是 集合上的dp 。每个集合的所包含的元素用二进制表示成一个数字。
由于每行最多有M最大为10个,并且每个位置只有两种状态(也就是 有炮兵 或者没有炮兵),于是用二进制数0-1023 来表示所有的状态。
二进制的10001,就表示在第1 个位置有炮兵,第5 个位置有炮兵的状态,表示成10进制的数就是17 。
因此就可以用 每个Integer类型的数就可以来表示一种状态,把一种状态压缩成了一个值,在通过DP 求解 ,就是状态压缩DP。
1。 首先是表示给出的 平地和高原的这张图 ,每一行的情况用 一个二进制数字表示,每一位表示一个点的情况。把p 看成数字0 ,把h 看成数字1 ,(为什么把h看成1 在后边)
那么第一行的二进制表示 0100 ,在转化为 十进制数就是 4。 于是4 就可以表示第一行的情况,3表示第二行的情况。
2 。 我们是把每一行看成一种状态,在列上做DP ,根据前两行的状态去推出这一行的状态。 可以用 f 来推出 表示当前的状态的最大炮兵数。具体这个数组该怎么开 就是个问题了。
我们可以 f[ r , i , j ] 来表示 当前第 r 行 ,此行状态为 i ,上一行的状态为 j 时 的最大的 炮兵数。
f[ r , i , j ] = max ( f[ r-1, j , t ] + bitnum[ i ]); // bitnum[ i ] 表示 i 的二进制的表达式中包含 1 的个数 。
i 表示 当前行的状态 , j 表示 上一行的状态 , t 表示 上一行的上一行的状态。
由于攻击范围是 2 个格子 ,因此 当前行 只会跟上两行有关系 , 就可以 i 状态的最大炮兵数, 跟 j ,t 有关,状态转移的方程 就要包括这三个参数。
如果只跟 j 有关的话, 就直接可以 f [ r, i ] = max( f[ r -1 , j] + bitnum[ i ]) ;
而这里 跟三个参数 有关 写 f [ r ,i ] = operator ( f[ r-1 ,j] , f[r-2,t] ) ; 似乎不可以有 两种状态推出 。
f[ r , i , j ] = max ( f[ r-1, j , t ] + bitnum[ i ]); f[ r , i , j ] 来表示 当前第 r 行 ,此行状态为 i ,上一行的状态为 j 时 的最大的 炮兵数。
这样f [ r ,i , j ] 就可以f [ r -1 , j , t ] 推出来,
当然由上一种状态推出下一种 状态的总炮兵的条件:这样只要 判定 i , j , t 三种状态表示的炮兵不会在攻击范围内 , 并且 当前状态 i 还要 符合地形。
3。 位运算 。 条件的判断 可以用到位运算 。
i 和 j 状态是否攻击 可以理解为 i 和 j 的相同位上 不同时 为 1 。 也就是 i & j ==0 就是合法状态。
同时 i 和当前的地形 也不能冲突 , 由于地形中 不可放炮兵的 用 1 来表示 ,对于地形为 k 时 , 只要 k & i == 0 就是 合法的 。
还有一点 就是 每种 状态是 0 - 1023 中 的一个数 ,但是有些状态 本身就是 不成立的 比如 二进制的 1 011 ,两个1 中间至少有两个 0 ,
于是 就可以把所有在行上的合法状态预处理出来,0-1023 之间的合法状态 的总和 为 60左右 ,可以写个程序跑出来。
在 60 中状态上 dp 要比1024 中状态上DP 效率高很多。
我们可以将这 60 种状态 在映射一下,
f[ r , i , j ] = max ( f[ r-1, j , t ] + bitnum[ i ]);
i = 1 时 表示 第一种合法的状态,j 表示第j 种 在行上合法的状态。因此就要预处理出所有的的合法状态,还可以预处理出每种合法状态的对应的 二进制中 1 的个数。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int max (int a,int b){
return (a>b)?a:b;
}
int n ,m;
int pst [120]; // 表示 第 i 行的 地形 的状态 。。。
int statenum; // 所有合法的状态的个数
int state[65]; // 存储 每行的 0 - 1023 之间的 所有合法的状态
int bitnum[65];//from 1 --> statenum ; // 存储 每种合法状态的 二进制表示中的 1 的个数
int f[120][65][65]; // dp
int bitsum =0 ;
bool judge(int num ){// 判断每种状态是否合法, 并且 bitsum 表示 当前状态二进制 中 1 的个数
int last =-3; // 二进制中 上一个 1 的位置
bitsum =0;
for(int i=0;i<=m;i++){
if(num>>i&1){
bitsum ++;
if(i-last<3) // 如果 位置的差 小于了 3 就是不合法
return false;
last = i;
}
}
return true;
}
void init(){ // 处理出 所有合法状态
int bignum = (1<<m) -1;
statenum = 0 ;
for(int i =0; i<=bignum;i++){
if(judge(i)){
statenum++;
state[statenum] = i;
bitnum[statenum] = bitsum;
}
}
}
void solve (){
memset(f,0,sizeof(f));
// dp的 初始 的状态。。。
for(int i =1 ;i<= statenum;i++){
if(!(state[i]&pst[1])){
f[1][i][1]= bitnum[i];
}
}
for(int r =2;r<=n;r++){
for(int i =1 ;i<=statenum ;i++){
if(state[i]&pst[r]) // 如果 i 和 第r 行的地形 不相容
continue;
for(int j =1 ;j<=statenum;j++){
if(state[i]&state[j]) // i 的状态 和 j 的状态 不相容
continue;
for(int t = 1;t<=statenum;t++){
if(state[i]&state[t])
continue;
// r row i current pow's state j表示上一行的状态 t 表示上一行的上一行的状态
f[r][i][j]=max(f[r][i][j],f[r-1][j][t]+bitnum[i]);
}
}
}
}
// 枚举所有状态找出最大值
int ans = 0;
for(int r =1 ;r<=n;r++){
for(int j =1 ;j<=statenum ;j++){
for(int i =1;i<=statenum;i++){
ans= max (ans,f[r][j][i]);
}
}
}
printf("%d\n",ans);
}
int main(){
scanf("%d%d",&n,&m);
memset(pst,0,sizeof(pst));
getchar();
for(int i= 1;i<=n;i++){
for(int j = 1;j<=m;j++){
char ch =getchar();
pst[i]= pst[i]<<1;
if(ch=='H'){
pst[i] +=1;
}
}
getchar();
}
init();
solve();
}
本文深入分析了一款军事战略游戏中的炮兵部署问题,旨在通过高效的算法优化,实现最大化的战场效能。文章详细介绍了如何利用状态压缩动态规划解决复杂地图上的炮兵部署挑战,确保在不相互攻击的情况下最大化部署数量。通过实例解析,读者能够掌握关键的算法思路和技巧。
607

被折叠的 条评论
为什么被折叠?



