状态dp经典题目。
题目问最多能放多少炮塔,那么,根据炮塔的特性,当前行能放的炮塔状态只跟上一行和上上一行有关,再往上的行无论怎么变化都不会影响当前行的情况。
于是,可以用一个二进制数来表示当前行的状态,每行十个位置便对应2^0到2^9,正好可以用一个int范围内整数表示状态。判断当前状态和之前一行有没有在同一列的,可以直接取与,两者相与如果结果非零,则肯定有在同一列的。
每行的状态可以通过预处理获得,同时可以获得此状态下放了多少炮塔。
具体请看代码,细细体会。
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int n,m;
int map[110];
int status[100];
int sum[100];
int cnt;
int dp[110][100][100];
bool ok(int i)
{
if ((i&(i<<1)))
return false;
if ((i&(i<<2)))
return false;
return true;
}
int getnum(int num)
{
int s=0;
while (num>0)
{
if (num&1)
s++;
num>>=1;
}
return s;
}
void find()
{
memset(status,0,sizeof(status));
memset(sum,0,sizeof(sum));
cnt=0;
for (int i=0;i<(1<<10);i++)
{
if (ok(i))
{
cnt++;
status[cnt]=i;
sum[cnt]=getnum(i);
}
}
}
int max(int a,int b)
{
return a>b?a:b;
}
int main()
{
find();
while (cin>>n>>m)
{
memset(map,0,sizeof(map));
memset(dp,-1,sizeof(dp));
for (int i=1;i<=n;i++)
{
for (int t=0;t<m;t++)
{
char p;
cin>>p;
if (p=='H')
map[i]=map[i]|(1<<t);
}
}
int mm=0;
for (int i=1;i<=cnt;i++)
{
if (status[i]<(1<<m))
mm=i;
else
break;
}
int ans=0;
for (int i=1;i<=mm;i++)
{
if ((status[i]&map[1])==0)
{
dp[1][i][1]=sum[i];
//cout<<status[i]<<" "<<dp[1][i][1]<<endl;
ans=max(ans,dp[1][i][1]);
}
}
//cout<<status[mm]<<endl;
for (int i=2;i<=n;i++)
{
for (int t=1;t<=mm;t++)
{
if (map[i]&status[t])
continue;
for (int k=1;k<=mm;k++)
{
if (map[i-1]&status[k])
continue;
if (status[t]&status[k])
continue;
for (int j=1;j<=mm;j++)
{
if (map[i-2]&status[j])
continue;
if (i>2&&(status[j]&status[k]))
continue;
if (status[t]&status[j])
continue;
if (dp[i-1][k][j]==-1)
continue;
//cout<<i<<" "<<status[t]<<" "<<status[k]<<" "<<status[j]<<endl;
dp[i][t][k]=max(dp[i][t][k],dp[i-1][k][j]+sum[t]);
ans=max(ans,dp[i][t][k]);
}
}
}
}
cout<<ans<<endl;
}
}