这题说实话还挺秀的,值得多看几次
在状态表示上,由于第 i 行的状态受到第 i−1 行和第 i−2 行的状态制约,因此我们表示状态的维度需要有两维,即
f[i][j][k] 表示考虑前 i 行,第 i 行的状态为 j ,第 i−1 行的状态为 k 的所有选法,其中 j, k 均为二进制数
属性为所有选法当中 1 的个数的最大值
在地图的处理上与 AcWing 327. 玉米田 一模一样,P
设置为 0,H
设置为 1,并用 g[i] 表示第 i 行的二进制数
关键在于状态的枚举上
与之前相同,我们用 heads 来存储可以相互转移的状态,与之前不同的是,之前的 heads 存储的是 states 当中的下标,这次直接存储二进制状态
那么对于第 i 行的二进制状态 j,我们照常枚举 states 即可,需要判断根 g[i] 是否有冲突
对于第 i−1 行的二进制状态 p1,我们从 heads[j] 中枚举,这时的 p1 与 j 一定没有冲突
对于第 i−2 行的二进制状态 p2,我们只能从 heads[p1] 中枚举,此时的 p2 可能会与 j 产生冲突,因此需要做额外的判断
完整代码:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 110, M = 1 << 10;
int f[2][M][M];
int g[N], cnt[M];
vector<int> states;
vector<int> heads[M];
int n, m;
bool check(int sta)
{
return !(sta & sta >> 1 || sta & sta >> 2);
}
int count(int sta)
{
int ans = 0;
while(sta)
ans += sta & 1, sta >>= 1;
return ans;
}
int main()
{
cin >> n >> m;
for(int i = 1, j = 0; i <= n; i++, j = 0)
for(char c; j < m && cin >> c; j++)
g[i] += (c == 'H') << j;
for(int i = 0; i < 1 << m; i++)
if(check(i))
states.push_back(i), cnt[i] = count(i);
for(int i = 0; i < states.size(); i++)
for(int j = 0; j < states.size(); j++)
if((states[i] & states[j]) == 0)
heads[states[i]].push_back(states[j]);
for(int i = 1; i <= n + 2; i++)
for(int j : states)//枚举第i行的所有合法状态
if((g[i] & j) == 0)//该状态是否与地图冲突
for(int p1 : heads[j])//第i-1行所有能够转移到第i行的状态
for(int p2 : heads[p1])//第i-2行所有能够转移到第i-1行的状态
if((j & p2) == 0)//i-2行的状态是否与第i行的状态等价
f[i & 1][j][p1] = max(f[i & 1][j][p1], f[i - 1 & 1][p1][p2] + cnt[j]);
cout << f[n + 2 & 1][0][0] << endl;
return 0;
}