Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 11808 | Accepted: 4266 |
Description
Input
Output
Sample Input
5 4
PHPP
PPHH
PPPP
PHPP
PHHP
Sample Output
6
Source
题目大意:给你一个有字母‘H’和字母‘P’组成的矩阵,你只能在字母‘P’的格点上放置炮兵,每个炮兵可以向上、下、左、右四个方向攻击2个单位的距离,要求你求出在该矩形中最多能够放置多少个炮兵。
分析:这种类似棋盘问题我们都一般可以用状态压缩的动态规划来做。
首先,这题和前面写过的一道Little Kings(sgu223)很像,我们把不同点列出来,然后就可以从那个程序扩展出来了
1、每一行只有特定区域可以放炮兵(由字母P组成的区域)
2、每一个炮兵影响的范围在同一行为左右4格,同一列为上下4格。
对于第一个不同点,我们可以用一个数组A记录下改行能够放置炮兵的二进制序列,(能放为0,不能放为1),对于我们DFS出来的状态S[j],只要(S[j] & A[i])==0即可满足条件.
对于第二个不同点,我们在用DFS扩展状态的时候,可以用一个变量SPC,记录当前列之前连续多少列没有放置炮兵,如果该数目大于等于2,那么就可以放置炮兵
另外,在进行状态转移的时候,也有不同
我们假设状态为F[I][J][K].代表当前位第I行,第I行的状态为S[J],第I-1行的状态为S[K],我们要满足
S[J] & A[I] ==0 , S[K] & (S[J] | A[I-1]) ==0 ,
然后我们再需要枚举第I-2行的状态S[L],如果(s[l] & (a[I-2] | s[K] | s[j] )加上f[i&1][j][k]<f[1-i&1][k][l]+c[j]便可以进行状态转移.
#include <cstdio>
using namespace std;
const int maxs = 61,maxn = 101,maxm = 11;
int s[maxs],c[maxs],a[maxn];
int f[2][maxs][maxs],ans,n,m;
char op[20];
void dfs(int p,int spc,int now,int cnt){
if (p==m) {s[++s[0]]=now;c[s[0]]=cnt;return ;}
dfs(p+1,spc+1,now*2,cnt);
if (spc>=2) dfs(p+1,0,now*2+1,cnt+1);
}
int main(){
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
scanf("%d%d\n",&n,&m);
dfs(0,2,0,0);
for (int i=1;i<=n;++i){
scanf("%s\n",op+1);
for (int t=1;t<=m;++t)
a[i]=a[i]*2+(op[t]=='H');
for (int j=1;j<=s[0];++j)
if (!(s[j] & a[i]))
for (int k=1;k<=s[0];++k)
if (!(s[k] & (a[i - 1] | s[j])))
for (int p=1;p<=s[0];++p)
if (!(s[p] & (a[i - 2] | s[j] | s[k])))
if (f[i & 1][j][k]<f[1 - i & 1][k][p] + c[j]){
f[i & 1][j][k] = f[1 - i & 1][k][p] + c[j];
if (f[i & 1][j][k] > ans) ans = f[i & 1][j][k];
}
}
printf("%d\n",ans);
return 0;
}