状压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;
}