思路:网上的解题思路已经有很多了,我就说说我的代码独特的地方。
1.关于这个三维dp的初始化问题,一般的思路就是初始化前两行,但是实际上初始化第1行也是可以的,只需要特判第二行,(然后实践证明不用特判也是行的,因为初始化为了0),本质好像都是一样的。
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int n, m, top;
char mp[105][15];
int state[1005];
int dp[105][105][105];//dp[i][j][k]表示第i行状态为k,第i-1行状态为j的时候的总数
int num[105], cur[105];
bool ok(int x) {//判断某一状态是不是合法解
if(x & (x << 1)) return 0;
if(x & (x << 2)) return 0;
return 1;
}
int judge(int x) {//判断某一十进制数的二进制形式有几个1
int cnt = 0;
while(x) {
cnt++;
x = x & (x - 1);
}
return cnt;
}
void init() {//预处理判断所有可行解,以及对应可行解的二进制1的个数(就是放大炮的数量)
top = 1;
int sum = 1 << m;
for(int i = 0; i < sum; i++) {
if(ok(i)) {
state[top++] = i;
}
}
for(int i = 1; i <= top; i++) {
num[i] = judge(state[i]);
}
}
bool fit(int x, int num) {//判断某一行能否变成对应的可行状态
if(state[x]&cur[num]) return 0;
return 1;
}
int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%s", mp[i] + 1);
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
if(mp[i][j] == 'H') cur[i] += (1 << (m - j));
}
}
init();
memset(dp, 0, sizeof(dp));
for(int i = 1; i <= top; i++) {
if(fit(i, 1)) dp[1][top][i] = num[i];//top是为能够遍历的到,1也是同样的效果
}
for(int i = 2; i <= n; i++) {
for(int j = 1; j <= top; j++) {
if(!fit(j, i)) continue;
for(int k = 1; k <= top; k++) {
if(!fit(k, i - 1)) continue;
if(state[j]&state[k]) continue;
for(int t = 1; t <= top; t++) {
if(i != 2) {
if(!fit(t, i - 2) ) continue;
if(state[t]&state[k]) continue;
if(state[t]&state[j]) continue;
dp[i][k][j] = max(dp[i][k][j], dp[i - 1][t][k] + num[j]);
}//如果当前是第二行,那么其实并不用考虑第二维的值,使用top是为了方便后面的计数,
//如果不用top也可以用1,因为1是每次都会存在的,因为每次都会有至少1组可行解
else dp[i][k][j] = max(dp[i][k][j], dp[i - 1][top][k] + num[j]);
}
}
}
}
int ans = 0;
for(int i = 1; i <= top; i++) {//循环判断最优解
for(int j = 1; j <= top; j++) {
ans = max(ans, dp[n][i][j]);
}
}
printf("%d\n", ans);
return 0;
}