题意:
求最大的炮兵摆放数量,并且使他们之间不相互误伤。
NOI 01 的题目。
思路:
1. 和 POJ 1038 类似,把行看成是一个整体,每一种摆放代表着一种状态,r-2, r-1 行的状态决定着 r 行的状态;
2. 本题状态比较稀疏,所以提前处理下炮兵的摆放无意是比较好的选择,由于没两个炮兵之间至少相距为 2,利用这个把每一行的炮兵可能摆放情况存放起来;
3. dp[r][i][j] 表示第 r 行的状态为 i 且第 r-1 行的状态为 j 时,所能摆放的最大炮兵数量,同时还需要枚举 r-2 行的炮兵摆放情况;
4. 最终有状态转移方程: dp[r][i][j] = max(dp[r][i][j], dp[r-1][j][k] + men[i]); 并且要求 dp[r-1][j][k] 存在,且 r 行能保证能摆放 i 状态;
5. 有了 POJ 1038 和 POJ 1185 的积累,我想以后做此类基于摆放的状态压缩 DP 应该能够有个比较清晰的思路了。
#include <iostream>
#include <algorithm>
using namespace std;
int row, col;
int state[60], cnt, men[60], mapstate[110];
bool grid[110][12];
int dp[2][60][60];
bool judge(int s) {
if (s & (s<<1))
return false;
if (s & (s<<2))
return false;
return true;
}
int calcmen(int s) {
int ans = 0;
while (s) {
ans += 1;
s &= (s-1);
}
return ans;
}
void initstate(int endstate) {
cnt = 0;
for (int s = 0; s < endstate; s++) {
if (judge(s)) {
state[cnt] = s;
men[cnt] = calcmen(s);
cnt += 1;
}
}
}
int solvedp() {
memset(dp[0], -1, sizeof(dp[0]));
for (int i = 0; i < cnt; i++) {
if (mapstate[0] & state[i])
continue;
dp[0][i][0] = men[i];
}
int T1 = 1, T2 = 0;
for (int r = 1; r < row; r++) {
T1 ^= 1, T2 ^= 1;
memset(dp[T2], -1, sizeof(dp[0]));
for (int i = 0; i < cnt; i++) {
if (state[i] & mapstate[r])
continue;
for (int j = 0; j < cnt; j++) {
if ((state[i] & state[j]) || (state[j] & mapstate[r-1]))
continue;
for (int k = 0; k < cnt; k++) {
if ((state[i] & state[k]) || (state[k] & state[j]))
continue;
if (dp[T1][j][k] != -1)
dp[T2][i][j] = max(dp[T2][i][j], dp[T1][j][k]+men[i]);
}
}
}
}
int ans = -1;
for (int i = 0; i < cnt; i++)
for (int j = 0; j < cnt; j++)
ans = max(ans, dp[T2][i][j]);
return ans;
}
int main() {
scanf("%d%d", &row, &col);
for (int i = 0; i < row; i++) {
char s[20];
scanf("%s", s);
mapstate[i] = 0;
for (int j = 0; j < col; j++) {
mapstate[i] <<= 1;
mapstate[i] |= s[j] == 'H' ? 1 : 0;
}
}
initstate(1<<col);
printf("%d\n", solvedp());
return 0;
}