状压DP
解题思路
首先对高山和平原做一个预处理,
再枚举
i
(
1
,
2
<
<
n
)
i(1,2<<n)
i(1,2<<n),将二进制数存在
a
[
i
]
a[i]
a[i] 里,将兵数存在
n
u
m
[
i
]
num[i]
num[i] 里。
然后开始DP
设
f
[
i
]
[
l
]
[
k
]
f[i][l][k]
f[i][l][k] 表示当第
i
i
i 行有
j
j
j 个炮兵,
i
i
i 的上一行有
k
k
k 点贡献。
则转移方程为
f
[
i
]
[
l
]
[
k
]
=
max
(
f
[
i
]
[
l
]
[
k
]
,
f
[
i
−
1
]
[
k
]
[
j
]
+
n
u
m
[
l
]
)
;
f[i][l][k]=\max(f[i][l][k],f[i-1][k][j]+num[l]);
f[i][l][k]=max(f[i][l][k],f[i−1][k][j]+num[l]);
这里我们不仅要判断是否可放炮兵,还要判断当前位置是不是山头。
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
int map[1010],a[1<<10],num[1<<10];
int n,m,tot,ans;
char c;
int lowbit(int x)
{
return x&(-x);
}
bool check(int x)
{
int cnt=0;
while(x)
{
if((x&1)&&cnt)
return 0;
if(x&1)
cnt=3;
if(cnt)
cnt--;
x>>=1;
}
return 1;
}
int count(int x)
{
int c=0;
for(int i=x; i>0; i-=lowbit(i))
c++;
return c;
}
int main()
{
cin>>n>>m;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
{
cin>>c;
if(c=='P')
map[i]=(map[i]<<1);
else
map[i]=(map[i]<<1)+1;
}
for(int i=0; i<1<<m; i++)
{
if(check(i))
{
a[++tot]=i;
num[tot]=count(i);
}
}
int f[110][tot+1][tot+1];
for(int i=1; i<=n; i++)
for(int j=1; j<=tot; j++)
if(!(a[j]&map[i-2]))
for(int k=1; k<=tot; k++)
if(!(a[j]&a[k])&&!(a[k]&map[i-1]))
for(int l=1; l<=tot; l++)
if(!(a[j]&a[l])&&!(a[k]&a[l])&&!(a[l]&map[i]))
f[i][l][k]=max(f[i][l][k],f[i-1][k][j]+num[l]);
for(int i=1; i<=tot; i++)
for(int j=1; j<=tot; j++)
ans=max(ans,f[n][i][j]);
cout<<ans;
return 0;
}