题目大意:给出一个地图,有两种点,P点可以站人,H点不能站人。每放一个人就会对他上下左右各两个格子产生影响,产生影响的格子不能放人。问最多能放多少个人。
思路:数据范围指引解题的方向。题中给出M<=10,这是一个很小的数字,2^10也不过才1024,用这个来dp就轻松多了。于是我们先预处理出每一行可能出现的状态,要注意一行中不能有两个距离<2。大表之后发现,每一行最多只能有60个左右。现在可以放心做O(n^3×m)的dp了。处理上下几行的关系的时候要注意status&status_==0时才能转移。
最后是如果m<=2的时候要特判,直接从状态中找一个最大值输出就可以。
CODE:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int m,n;
char src[110][110];
int status[110][1500];
int num[1500];
int f[110][110][110];
void Pretreatment()
{
for(int i = 1; i <= (1 << n); ++i)
num[i] = num[i >> 1] + (i&1);
}
inline bool Judge(int x,int status)
{
static bool v[110];
memset(v,false,sizeof(v));
int p = 0;
while(status) {
v[p++] = status&1;
status >>= 1;
}
for(int i = 0; i < n; ++i)
if(v[i]) {
if(src[x][i] == 'H') return false;
if(v[i - 1] || v[i - 2]) return false;
}
return true;
}
int main()
{
cin >> m >> n;
Pretreatment();
for(int i = 1; i <= m; ++i)
scanf("%s",src[i]);
for(int i = 1; i <= m; ++i)
for(int j = 0; j < (1 << n); ++j)
if(Judge(i,j))
status[i][++status[i][0]] = j;
for(int j = 1; j <= status[2][0]; ++j)
for(int i = 1; i <= status[1][0]; ++i)
if((status[1][i]&status[2][j]) == 0)
f[2][j][i] = max(f[2][j][i],num[status[1][i]] + num[status[2][j]]);
if(m == 1 || m == 2) {
int ans = 0;
for(int i = 1; i <= status[1][0]; ++i)
ans = max(ans,num[status[1][i]]);
for(int i = 1; i <= status[2][0]; ++i)
ans = max(ans,num[status[2][i]]);
cout << ans << endl;
return 0;
}
int ans = 0;
for(int i = 3; i <= m; ++i)
for(int j = 1; j <= status[i][0]; ++j)
for(int k = 1; k <= status[i - 1][0]; ++k)
for(int l = 1; l <= status[i - 2][0]; ++l) {
if((status[i][j]&status[i - 1][k]) || (status[i - 1][k]&status[i - 2][l]) || (status[i][j]&status[i - 2][l])) continue;
f[i][j][k] = max(f[i][j][k],f[i - 1][k][l] + num[status[i][j]]);
ans = max(ans,f[i][j][k]);
}
cout << ans << endl;
return 0;
}