【题解】洛谷P2704

题目传送门

考虑到地形可以用二进制01表示,使用状压dp。

状压dp是采用十进制数字表示二进制的状态,实现状态的压缩。

如何设计状态,dp[层数][当前行的放置炮兵的情况][上一行放置炮兵的情况。

判断炮兵的放置情况要判断下面的情况是否合法:

  • 左移右移一位两位相与的结果要为0
  • 放置情况与地形相与的结果要为0

我们可以通过下面的代码来实现记录地形(grd数组)。

1
2
3
4
5
6
7
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
char tmp;
cin>>tmp;
grd[i]=(grd[i]<<1)+(tmp=='H');
}

为了不误伤友军,我们需要通过二进制的左移和右移来判断是否会误伤。

我们用以下的代码,通过can数组来记录这个状态是否可用。

1
2
3
4
5
6
for(int i=0;i<(1<<m);i++)
if(!(i&(i<<1))&&!(i&(i<<2))&&!(i&i>>1)&&!(i&i>>2))
{
can[i]=1;
if((i&grd[1])==0)dp[1][i][0]=res(i);
}

我们还可以顺便把dp的第一层给搞定。

res(i)是用来计算i在二进制下1的个数。

1
2
3
4
5
6
7
8
9
10
int res(int i)
{
int cnt=0;
while(i)
{
if(i&1)cnt++;
i>>=1;
}
return cnt;
}

同理,我们通过枚举上一行和当前行的状态来进行dp。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//对第二行进行dp
for(int A=0;A<(1<<m);A++)
if(can[A]&&(A&grd[1])==0)
for(int B=0;B<(1<<m);B++)
if(can[B]&&(B&grd[2])==0&&(A&B)==0)
dp[2][B][A]=dp[1][A][0]+res(B);
//对3~n行进行dp
for(int i=3;i<=n;i++)
for(int A=0;A<(1<<m);A++)
if(can[A]&&(A&grd[i-2])==0)
for(int B=0;B<(1<<m);B++)
if(can[B]&&(B&grd[i-1])==0&&(A&B)==0)
for(int C=0;C<(1<<m);C++)
if(can[C]&&(C&grd[i])==0&&(C&A)==0&&(C&B)==0)
dp[i][C][B]=max(dp[i][C][B],dp[i-1][B][A]+res(C));

后面用ans统计答案。

1
2
3
4
for(int i=1;i<=n;i++)
for(int j=0;j<(1<<m);j++)
for(int k=0;k<(1<<m);k++)
ans=max(ans,dp[i][j][k]);

相信大家的拼装能力肯定不错,能够把上面这些零件拼起来的。

完整答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include<bits/stdc++.h>
using namespace std;
int n,m,grd[110],dp[110][(1<<10)][(1<<10)];
bool can[(1<<10)];
int res(int i)
{
int cnt=0;
while(i)
{
if(i&1)cnt++;
i>>=1;
}
return cnt;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
char tmp;
cin>>tmp;
grd[i]=(grd[i]<<1)+(tmp=='H');
}
for(int i=0;i<(1<<m);i++)
if(!(i&(i<<1))&&!(i&(i<<2))&&!(i&i>>1)&&!(i&i>>2))
{
can[i]=1;
if((i&grd[1])==0)dp[1][i][0]=res(i);
}

for(int A=0;A<(1<<m);A++)
if(can[A]&&(A&grd[1])==0)
for(int B=0;B<(1<<m);B++)
if(can[B]&&(B&grd[2])==0&&(A&B)==0)
dp[2][B][A]=dp[1][A][0]+res(B);
for(int i=3;i<=n;i++)
for(int A=0;A<(1<<m);A++)
if(can[A]&&(A&grd[i-2])==0)
for(int B=0;B<(1<<m);B++)
if(can[B]&&(B&grd[i-1])==0&&(A&B)==0)
for(int C=0;C<(1<<m);C++)
if(can[C]&&(C&grd[i])==0&&(C&A)==0&&(C&B)==0)
dp[i][C][B]=max(dp[i][C][B],dp[i-1][B][A]+res(C));
int ans=0;
for(int i=1;i<=n;i++)
for(int j=0;j<(1<<m);j++)
for(int k=0;k<(1<<m);k++)
ans=max(ans,dp[i][j][k]);
cout<<ans;
return 0;
}

【题解】洛谷P2704
http://j27egu.github.io/2025/08/21/【题解】洛谷P2704/
作者
j27eGU
发布于
2025年8月21日
许可协议